Можно ли в сборке x86 удалить значение из стека без его сохранения? Что-то вроде pop word null
? Я, очевидно, мог бы использовать add esp,4
, но, может быть, мне не хватает красивой и чистой мнемоники cisc?
Сборка x86: извлечь значение без его сохранения
Ответы (1)
add esp,4
/ add rsp,8
это нормальный/идиоматический/чистый способ. Никакого специального способа не требуется, потому что стеки не являются волшебными или особенными (по крайней мере, в этом отношении); это просто указатель в регистре с некоторыми инструкциями, которые используют его неявно. (А для стеков ядра прерывания используют его асинхронно, поэтому программное обеспечение не может реализовать красную зону ядра, даже если бы захотело...)
Помимо этого, волшебный способ CISC для очистки всего кадра стека в конце функции — это leave
= mov esp, ebp
/ pop ebp
(или 16- или 64-битный эквивалент). В отличие от enter
, он достаточно быстр на современных процессорах, чтобы его можно было использовать на практике, но все еще является инструкцией 3 uop для процессоров Intel. (http://agner.org/optimize/). Но leave
работает только в том случае, если вы потратили дополнительные инструкции на создание кадра стека с ebp
/ rbp
в первую очередь. (Обычно вы бы этого не сделали, если только вам не нужно зарезервировать переменный объем пространства стека, например, с push
в цикле для создания массива или эквивалентом C99 VLA или alloca
. Или для начального кода, чтобы получить доступ к locals проще или в 16-битном режиме, где SP
нельзя использовать в режимах адресации.)
Волшебный способ CISC для очистки аргументов стека заключается в том, чтобы вызываемый объект использовал ret imm16
. (стоимостью 1 дополнительная моп) для извлечения аргументов, создавая соглашение о вызовах, при котором вызываемый объект очищает стек. В соглашении о вызовах вызывающего абонента нет возможности использовать эту форму ret
, но вы можете просто оставить смещение стека и использовать mov
для хранения аргументов для следующего вызова функции вместо push
(если функции нужны какие-либо аргументы стека в all; соглашения о вызовах register-arg обычно более эффективны.)
Таким образом, волшебные способы CISC не имеют преимущества в производительности на современных процессорах, только небольшой размер кода.
Есть 2 причины, по которым вы можете использовать pop reg
вместо add esp,4
:
- code-size:
pop r32/r64
— это однобайтовая инструкция, а не 3 байта дляadd esp,4
или 4 байта дляadd rsp,8
. производительность: механизм стека Intel должен вставлять дополнительные операции синхронизации стека, когда вы используете
esp
/rsp
явно после инструкции стека (push/pop/call/ret). Таким образом, послеcall
(которое возвращается сret
) он сохраняет uop для использованияpop
вместоadd esp,4
до того, как выret
в конце функции.Механизм стека AMD не нуждается в дополнительных операциях синхронизации стека, но по-прежнему выполняет однократные инструкции push/pop. В отличие от старых процессоров Intel/AMD, где push/pop стоит больше, чем простая
mov
загрузка/сохранение, требуется отдельная uop для модификации указателя стека. И создание зависимости данных от указателя стека.
См. Почему помещает ли эта функция RAX в стек в качестве первой операции? подробнее о производительности.
Если вы стремились к эстетике, вы можете сделать отступ, отформатировать и красиво прокомментировать свой код, но помимо вы выбрали неправильный язык, когда выбрали x86 asm, если эстетика перевешивает оптимизацию.
Конечно, если вам нужно настроить стек более чем на 1 ширину регистра, обязательно используйте add
, если вам не нужны данные, которые загрузит pop
. Или, если вам нужно изменить его на +128 байт, используйте sub esp, -128
, потому что -128
кодируется как расширенный знак imm8, а +128 — нет.
Или, может быть, использовать lea esp, [esp+4]
, как это делает gcc с -mtune=atom
. (Для упорядоченного атома, а не для сильвермонта). Как я уже сказал, если вы хотели чистоты, вам не следовало выбирать x86 asm.
Вы почти всегда можете найти мертвый регистр, чтобы pop
в него. Если вам нужно настроить E/RSP на один слот стека перед извлечением некоторых регистров, которые вы действительно хотели извлечь, вы всегда можете извлечь один и тот же регистр дважды.
В крайне редких случаях, когда ни один из 7 (x86-32) или 15 (x86-64) нестековых регистров недоступен в качестве назначения pop
, эта оптимизация недоступна, и вам следует просто использовать традиционный add
.< /strong> Не стоит тратить дополнительные инструкции, чтобы сделать возможным pop
; это перевешивало бы незначительное преимущество использования pop
.
Обратите внимание, что pop Sreg
(сегментный регистр) по-прежнему использует обычную «ширину стека» (32 или 64 бита, в зависимости от режима), а не только 16 для 16-битного регистра. Но только pop ds/es/ss
являются однобайтовыми. pop fs/gs
— по 2 байта каждый. Поэтому, если вы оптимизируете размер кода, pop gs
будет на 1 байт меньше, чем add esp,4
, но намного медленнее. (Или на 2 байта меньше, чем add rsp,8
).
leave
и ret imm16
можно квалифицировать как волшебный CISC.
- person Peter Cordes; 09.02.2018
add esp,4
для 32-битной версии иadd rsp,8
для 64-битной. - person Rudy Velthuis   schedule 09.02.2018