Как сборка этой функции реализует условие?

Следующий код,

int foo(int);
int bar(int);

int foobar(int i) {
    int a = foo(i);
    int b = bar(i);
    return a == b ? a : b;
};

с магистралью GCC компилируется в эту сборку:

foobar(int):
        push    rbx
        mov     ebx, edi
        call    foo(int)
        mov     edi, ebx
        pop     rbx
        jmp     bar(int)

Вот оно вживую.

Этот TU понятия не имеет, что i ему будет передано, и он понятия не имеет, какие foo и bar вернутся, поэтому он понятия не имеет, будет ли a == btrue или false. Так что это должно проверьте выходные данные двух вызовов и каким-то образом сравните их, чтобы решить, какой из них следует вернуть.

Но я не вижу этого в собрании. Что мне не хватает?

Состояние исчезает, return a == b ? a : b; оптимизировано до return b;

3CxEZiVlQ 07.06.2024 17:31

@3CxEZiVlQ, а почему?

Enlico 07.06.2024 17:31

если a==b, то возврат a аналогичен возврату b.

Scott Hunter 07.06.2024 17:34

@Enlico Подумайте о возможных случаях. У вас есть 2 варианта: a == b, в этом случае не важно, что вы возвращаете, или a != b, в этом случае вы возвращаете b. Компилятор должен вызвать foo в случае возникновения побочных эффектов, но он может безопасно оптимизировать условие до return b;, поскольку оно удовлетворяет обоим случаям.

Yksisarvinen 07.06.2024 17:34

@Evg, я экспериментировал с ссылочной прозрачностью (godbolt.org/z/oqxGbGMMv) и чрезмерно упростил этот пример, поэтому в итоге задал этот вопрос. Если бы я поставил > вместо ==, я бы даже не задавал вопрос. :D

Enlico 07.06.2024 17:48

@Enlico Хаха, нет... Оооочень умно... я имею в виду компилятор :)

Pepijn Kramer 07.06.2024 18:06
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
20
6
870
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

return a == b ? a : b; то же, что return a == b ? b : b; то же самое, что return b;.

Работает с целыми числами... Интересно, что будет с числами с плавающей запятой, где a==b не означает, что a совпадает с b

Swift - Friday Pie 07.06.2024 17:56

@Swift-FridayPie достаточно легко увидеть, изменив типы в godbolt. С gcc -fno-signed-zeros (или -ffast-math, что подразумевает это) он компилируется идентично версии int, потому что 0 и -0 — единственные числа с плавающей запятой, которые сравниваются равными, но не являются идентичными. Без этого флага вы получите «простую» версию со сравнением и переходом.

hobbs 09.06.2024 04:36

clang также делает то же самое с -fno-signed-zeros, но без него генерирует версию SSE/AVX без ветвей.

hobbs 09.06.2024 04:45

Рассмотрим два случая:

(1) a равно b.

Тогда foo(i) и bar(i) нужно вызывать в этом порядке. Код делает это. Поскольку возвращаемое значение bar(i) такое же, как и у foo(i), достаточно вызвать bar в хвостовом вызове и позволить его результату вернуться вызвавшему foobar.

(2) a не равно b.

Тогда foo(i) и bar(i) нужно вызывать в этом порядке. Код делает это. Поскольку возвращаемое значение bar(i) отличается от значения foo(i), b, т. е. возвращаемое значение bar(i) должно быть возвращено вызывающей стороне. Этого можно добиться с помощью хвостового вызова bar и вернуть результат вызывающему foobar.

Оба случая делают одно и то же.

Это было бы более полезно, если бы не скрывало (важный) момент хвостового вызова.

Scott Hunter 07.06.2024 17:40

@ScottHunter В тексте дважды содержится «хвостовой вызов», не так ли?

j6t 07.06.2024 17:45

Конечно, похоронен среди лишнего текста, поэтому я сказал «непонятно».

Scott Hunter 07.06.2024 17:52

Другие вопросы по теме