Есть ли преимущество чтения данных без использования всплывающей операции? - PullRequest
0 голосов
/ 05 апреля 2019

Согласно этому документу PDF (стр. 66) , следующая группа утверждений

mov eax, DWORD PTR SS:[esp]
mov eax, DWORD PTR SS:[esp + 4]
mov eax, DWORD PTR SS:[esp + 8]

эквивалентна следующей группе утверждений:

pop eax
pop eax
pop eax

Есть ли преимущество первого над вторым?

Ответы [ 2 ]

1 голос
/ 06 апреля 2019

mov оставляет данные в стеке, pop удаляет их, поэтому вы можете прочитать их только один раз и только по порядку.Данные ниже ESP должны считаться «потерянными», если только вы не используете соглашение о вызовах / ABI, включающее красную зону под указателем стека.

Данные , обычно , все еще там, нижеESP, но асинхронные вещи, такие как обработчики сигналов или отладчик, оценивающий call fflush(0) в контексте вашего процесса, могут наступить на него.

Кроме того, pop изменяет ESP, поэтому каждый pop требует стека-unwind метаданные 1 в другом разделе исполняемого файла / библиотеки, чтобы он был полностью ABI-совместимым с SEH в Windows или i386 / x86-64 System V ABI в других ОС (который указывает, что все функции должныраскрутить метаданные, даже если они не являются функциями C ++, которые на самом деле поддерживают распространение исключений).


Но если вы читаете данные в последний раз и вам все это нужно, тогдада, pop - это эффективный способ чтения на современных процессорах (например, Pentium-M и более поздних, с механизмом стека для обработки обновлений ESP без отдельной операции.)

На старых процессорах, таких как Pentium III, pop был на самом деле медленнее, чем 3x mov + add esp,12, и компиляторы генерировали код, как показывает ответ Брендана.

void foo() {
    asm("" ::: "ebx", "esi", "edi");
}

Эта функция заставляет компиляторсохранить / восстановить 3 сохраненных вызовом регистра (объявив на них клобберы.) На самом деле на самом деле их не трогает;встроенная строка asm пуста.Но это позволяет легко увидеть, что компиляторы будут делать для сохранения / восстановления.(Это единственный раз, когда они будут использовать pop в обычном режиме.)

GCC по умолчанию (tune = generic) code-gen , или, например, с -march=skylake, выглядит следующим образом( из проводника компилятора Godbolt )

foo:                        # gcc8.3 -O3 -m32
        push    edi
        push    esi
        push    ebx
        pop     ebx
        pop     esi
        pop     edi
        ret

Но указание настроить старый ЦП без стекового движка делает это следующим образом:

foo:                     # gcc8.3  -march=pentium3 -O3 -m32
        sub     esp, 12
        mov     DWORD PTR [esp], ebx
        mov     DWORD PTR [esp+4], esi
        mov     DWORD PTR [esp+8], edi
        mov     ebx, DWORD PTR [esp]
        mov     esi, DWORD PTR [esp+4]
        mov     edi, DWORD PTR [esp+8]
        add     esp, 12
        ret

gcc считает, что -march=pentium-m не имеет стекового движка или, по крайней мере, решает не использовать push/pop там.Я думаю, что это ошибка, потому что микроарх Agner Fog pdf определенно описывает движок стека, присутствующий в Pentium-M.

На PM и более поздних версиях push / pop - это однопроцессные инструкции,с обновлением ESP, обрабатываемым вне внешнего сервера, и для push микросхемы адреса хранения + данных хранилища микроплавленны.

В Pentium 3 по 2 или 3 монеты в каждом.(Опять же, смотрите таблицы инструкций Agner Fog.)

На P5 Pentium в порядке, push и pop на самом деле в порядке.(Но инструкций назначения памяти, таких как add [mem], reg, обычно избегали, потому что P5 не разбивал их на мопы, чтобы лучше транслировать.)


Смешивание pop с прямыми ссылками на [esp] будет фактическибыть потенциально медленнее, чем один или другой, на современных процессорах Intel, потому что это требует дополнительных операций синхронизации стека.


Очевидно, что запись EAX 3 раза подряд означает, что первые 2 загрузки бесполезны в обоих случаях.Последовательности.

См. Extreme Fibonacci для примера, как pop (1 моп или как 1.1 моп с амортизированной синхронизацией стека) более эффективен, чем lodsd (2 мопа на Skylake) для чтениячерез массив.(В злом коде, который предполагает большую красную зону, потому что он не устанавливает обработчики сигналов. На самом деле не делайте этого, если вы точно не знаете, что делаете, и когда он сломается; это скорее глупые компьютерные уловки /экстремальная оптимизация для кода-гольфа, чем все, что практически полезно.)


Сноска 1: Проводник компилятора Godbolt обычно отфильтровывает дополнительные директивы ассемблера, но если вы снимите этот флажок, вы можетесм. функцию gcc, которая использует push / pop, имеет .cfi_def_cfa_offset 12 после каждого push / pop.

        pop     ebx
        .cfi_restore 3
        .cfi_def_cfa_offset 12
        pop     esi
        .cfi_restore 6
        .cfi_def_cfa_offset 8
        pop     edi
        .cfi_restore 7
        .cfi_def_cfa_offset 4

Директивы метаданных .cfi_restore 7 должны присутствовать независимо от push / pop и mov, потому что это позволяет стекуразматывать восстановить сохраненные вызовы регистры по мере раскрутки.(7 - номер регистра).

Но для других применений push / pop внутри функции (например, передача аргументов в вызов функции или фиктивная pop для ее удаления из стека) у вас не будет .cfi_restore, только метаданные дляуказатель стека изменяется относительно стекового фрейма.

Обычно вы не беспокоитесь об этом в рукописном асме, но компиляторы должны сделать это правильно, поэтому есть небольшая дополнительная плата за использование push/popс точки зрения общего размера исполняемого файла. Но только в тех частях файла, которые обычно не отображаются в память и не смешиваются с кодом.

0 голосов
/ 05 апреля 2019

Это:

pop eax
pop ebx
pop ecx

.. в некотором роде эквивалентно этому:

mov eax,[esp]
add esp,4

mov ebx,[esp]
add esp,4

mov ecx,[esp]
add esp,4

.. что может быть так:

mov eax,[esp]     ;Do this instruction
add esp,4         ; ...and this instruction in parallel

                   ;Stall until the previous instruction completes (and the value
mov ebx,[esp]      ;in ESP becomes known); then do this instruction
add esp,4          ; ...and this instruction in parallel

                   ;Stall until the previous instruction completes (and the value
mov ecx,[esp]      ;in ESP becomes known); then do this instruction
add esp,4          ; ...and this instruction in parallel

Дляэтот код:

mov eax, [esp]
mov ebx, [esp + 4]
mov ecx, [esp + 8]
add esp,12

.. все инструкции могут выполняться параллельно (теоретически).

Примечание: на практике все вышеперечисленное зависит от того, какой процессор и т. д.

...