Это дурацкий неоптимизированный код, потому что вы скомпилировали с -O0
(скомпилируйте быстро, пропустите большинство этапов оптимизации).Устаревшая установка / очистка стекового фрейма - это просто шум.Аргумент находится в стеке прямо над адресом возврата, то есть на 4(%esp)
при входе в функцию(См. Также Как удалить «шум» из выходных данных сборки GCC / clang? )
Удивительно, что компилятор использует 3 инструкции для умножения путем сдвига и добавления вместоimull $34, 4(%esp), %eax
/ ret
, если не настраивать старые процессоры.2 инструкции являются отсечкой для современных gcc и clang с настройками по умолчанию.См., Например, Как умножить регистр на 37, используя только 2 последовательные инструкции leal в x86?
Но это можно сделать с двумя инструкциями, используя LEA (не считаяmov
для копирования реестра);код раздут, потому что вы скомпилировали без оптимизации.(Или вы настроились на старый процессор, где, возможно, есть какая-то причина избегать LEA.)
Я думаю, вы, должно быть, использовали для этого gcc;отключение оптимизации с другими компиляторами всегда просто использует imul
для умножения на не-степень-2.Но я не могу найти версию gcc + опции в проводнике компилятора Godbolt, который выдает именно ваш код.Я не пробовал все возможные комбинации.MSVC 19.10 -O2
использует тот же алгоритм, что и ваш код, включая двойную загрузку a
.
Компиляция с gcc5.5 (который является новейшим gcc, который не просто использует imul
, даже при -O0
), мы получаем что-то вроде вашего кода, но не совсем.(Те же операции в другом порядке, без двойной загрузки a
из памяти).
# gcc5.5 -m32 -xc -O0 -fverbose-asm -Wall
func:
pushl %ebp #
movl %esp, %ebp #, # make a stack frame
movl 8(%ebp), %eax # a, tmp89 # load a from the stack, first arg is at EBP+8
addl %eax, %eax # tmp91 # a*2
movl %eax, %edx # tmp90, tmp92
sall $4, %edx #, tmp92 # a*2 << 4 = a*32
addl %edx, %eax # tmp92, D.1807 # a*2 + a*32
popl %ebp # # clean up the stack frame
ret
Компиляция с оптимизацией с той же более старой версией GCC на проводник компилятора Godbolt : gcc5.5 -m32 -O3 -fverbose-asm
, мы получаем:
# gcc5.5 -m32 -O3. Also clang7.0 -m32 -O3 emits the same code
func:
movl 4(%esp), %eax # a, a # load a from the stack
movl %eax, %edx # a, tmp93 # copy it to edx
sall $5, %edx #, tmp93 # edx = a<<5 = a*32
leal (%edx,%eax,2), %eax # eax = edx + eax*2 = a*32 + a*2 = a*34
ret # with a*34 in EAX, the return-value reg in this calling convention
С gcc 6.x или новее мы получаем этот эффективный asm : imul
-после того, как источник памяти декодирует только один микроплавкий уоп на современных процессорах Intel, а целочисленное умножение имеет только 3 цикла задержки для Intel со времен Core2 и AMD со времен Ryzen.(https://agner.org/optimize/).
# gcc6/7/8 -m32 -O3 default tuning
func:
imull $34, 4(%esp), %eax #, a, tmp89
ret
Но с -mtune=pentium3
мы, как ни странно, не получаем LEA . Это похоже на пропущенную оптимизацию. LEA имеет задержку в 1 цикл на Pentium 3/ Pentium-M.
# gcc8.2 -O3 -mtune=pentium3 -m32 -xc -fverbose-asm -Wall
func:
movl 4(%esp), %edx # a, a
movl %edx, %eax # a, tmp91
sall $4, %eax #, tmp91 # a*16
addl %edx, %eax # a, tmp92 # a*16 + a = a*17
addl %eax, %eax # tmp93 # a*16 * 2 = a*34
ret
Это то же самое, что и ваш код, но использует reg-reg mov
вместо перезагрузки из стека, чтобы добавить a
к результату сдвига.