Обойти Windows, вызывая соглашение, сохраняющее регистры xmm? - PullRequest
0 голосов
/ 17 мая 2019

Есть ли в Windows способ обойти требование сохранения регистров XMM в вызове функции (кроме записи всего этого в сборке)

У меня есть много встроенных функций AVX2, которые, к сожалению, раздуты этим.

В качестве примера это будет размещено компилятором (MSVC) вверху функции:

00007FF9D0EBC602 vmovaps xmmword ptr [rsp + 1490h], xmm6
00007FF9D0EBC60B vmovaps xmmword ptr [rsp + 1480h], xmm7
00007FF9D0EBC614 vmovaps xmmword ptr [rsp + 1470h], xmm8
00007FF9D0EBC61D vmovaps xmmword ptr [rsp + 1460h], xmm9
00007FF9D0EBC626 vmovaps xmmword ptr [rsp + 1450h], xmm10
00007FF9D0EBC62F vmovaps xmmword ptr [rsp + 1440h], xmm11
00007FF9D0EBC638 vmovaps xmmword ptr [rsp + 1430h], xmm12
00007FF9D0EBC641 vmovaps xmmword ptr [rsp + 1420h], xmm13
00007FF9D0EBC64A vmovaps xmmword ptr [rsp + 1410h], xmm14
00007FF9D0EBC653 vmovaps xmmword ptr [rsp + 1400h], xmm15

А потом в конце функции ..

00007FF9D0EBD6E6 vmovaps xmm6, xmmword ptr [r11-10h]
00007FF9D0EBD6EC vmovaps xmm7, xmmword ptr [r11-20h]
00007FF9D0EBD6F2 vmovaps xmm8, xmmword ptr [r11-30h]
00007FF9D0EBD6F8 vmovaps xmm9, xmmword ptr [r11-40h]
00007FF9D0EBD6FE vmovaps xmm10, xmmword ptr [r11-50h]
00007FF9D0EBD704 vmovaps xmm11, xmmword ptr [r11-60h]
00007FF9D0EBD70A vmovaps xmm12, xmmword ptr [r11-70h]
00007FF9D0EBD710 vmovaps xmm13, xmmword ptr [r11-80h]
00007FF9D0EBD716 vmovaps xmm14, xmmword ptr [r11-90h]
00007FF9D0EBD71F vmovaps xmm15, xmmword ptr [r11-0A0h]

Это 20 инструкций, которые ничего не делают, так как мне не нужно сохранять состояние XMM. У меня есть 100 из этих функций, которые компилятор раздувает, как это. Все они вызываются с одного и того же сайта вызова с помощью указателей функций.

Я пытался изменить соглашение о вызовах (__ vectorcall / cdecl / fastcall), но это, похоже, ничего не делает.

1 Ответ

2 голосов
/ 17 мая 2019

Используйте соглашение о вызовах x86-64 System V для своих вспомогательных функций , которые вы хотите соединить с помощью указателей на функции. В этом соглашении о вызовах все xmm / ymm0..15 и zmm0..31 являются замкнутыми при вызове, поэтому даже вспомогательные функции, которым требуется более 5 векторных регистров, не должны сохранять или восстанавливать их.

Функция внешнего интерпретатора, которая их вызывает, должна по-прежнему использовать быстрый вызов Windows x64 или векторный вызов, поэтому извне она полностью соблюдает это соглашение о вызовах.

Это поднимет все операции сохранения / восстановления XMM6..15 в этот вызывающий объект вместо каждой вспомогательной функции. Это уменьшает размер статического кода и амортизирует затраты времени выполнения для нескольких вызовов через указатели функций.


AFAIK, MSVC не поддерживает функции маркировки, поскольку используется соглашение о вызовах x86-64 System V, только fastcall против vectorcall, поэтому вам придется использовать clang .

(ICC содержит ошибки и не может сохранить / восстановить XMM6..15 вокруг вызова функции System V ABI).

Windows GCC глючит с 32-байтовым выравниванием стека для разливов __m256, поэтому в общем случае небезопасно использовать GCC с -march= для всего, что включает AVX.


Используйте __attribute__((sysv_abi)) или __attribute__((ms_abi)) в объявлениях функций и указателей на функции.

Я думаю ms_abi это __fastcall, а не __vectorcall. Clang также может поддерживать __attribute__((vectorcall)), но я не пробовал. Результаты Google в основном представляют собой запросы / обсуждения функций.

void (*helpers[10])(float *, float*) __attribute__((sysv_abi));

__attribute__((ms_abi))
void outer(float *p) {
    helpers[0](p, p+10);
    helpers[1](p, p+10);
    helpers[2](p+20, p+30);
}

компилируется следующим образом на Godbolt с лязгом 8.0 -O3 -march=skylake. (gcc / clang в Godbolt предназначается для Linux, но я использовал явные ms_abi и sysv_abi как для функции, так и для указателей на функции, так что код gen не зависит от того, что по умолчанию установлено значение sysv_abi. Очевидно, вы ' Я хочу создать вашу функцию с помощью Windows gcc или clang, чтобы при вызове других функций использовалось правильное соглашение о вызовах. И полезный формат объектного файла и т. д.)

Обратите внимание, что gcc / clang генерирует код для outer(), который ожидает входящий указатель arg в RCX (Windows x64), но передает его вызываемым абонентам в RDI и RSI (x86-64 System V).

outer:                                  # @outer
        push    r14
        push    rsi
        push    rdi
        push    rbx
        sub     rsp, 168
        vmovaps xmmword ptr [rsp + 144], xmm15 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 128], xmm14 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 112], xmm13 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 96], xmm12 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 80], xmm11 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 64], xmm10 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 48], xmm9 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 32], xmm8 # 16-byte Spill
        vmovaps xmmword ptr [rsp + 16], xmm7 # 16-byte Spill
        vmovaps xmmword ptr [rsp], xmm6 # 16-byte Spill
        mov     rbx, rcx                            # save p 
        lea     r14, [rcx + 40]
        mov     rdi, rcx
        mov     rsi, r14
        call    qword ptr [rip + helpers]
        mov     rdi, rbx
        mov     rsi, r14
        call    qword ptr [rip + helpers+8]
        lea     rdi, [rbx + 80]
        lea     rsi, [rbx + 120]
        call    qword ptr [rip + helpers+16]
        vmovaps xmm6, xmmword ptr [rsp] # 16-byte Reload
        vmovaps xmm7, xmmword ptr [rsp + 16] # 16-byte Reload
        vmovaps xmm8, xmmword ptr [rsp + 32] # 16-byte Reload
        vmovaps xmm9, xmmword ptr [rsp + 48] # 16-byte Reload
        vmovaps xmm10, xmmword ptr [rsp + 64] # 16-byte Reload
        vmovaps xmm11, xmmword ptr [rsp + 80] # 16-byte Reload
        vmovaps xmm12, xmmword ptr [rsp + 96] # 16-byte Reload
        vmovaps xmm13, xmmword ptr [rsp + 112] # 16-byte Reload
        vmovaps xmm14, xmmword ptr [rsp + 128] # 16-byte Reload
        vmovaps xmm15, xmmword ptr [rsp + 144] # 16-byte Reload
        add     rsp, 168
        pop     rbx
        pop     rdi
        pop     rsi
        pop     r14
        ret

GCC делает в основном один и тот же код. Но в Windows GCC глючит AVX.

ICC19 создает аналогичный код, но без сохранения / восстановления xmm6..15. Это ошибка showtopper; если кто-либо из вызывающих абонентов do ударит эти регистры так, как им разрешено, то возврат из этой функции нарушит соглашение о вызовах.

Это оставляет clang единственным компилятором, который вы можете использовать . Все в порядке; лязг очень хорош.


Если вашим абонентам не нужны все регистры YMM, сохранение / восстановление всех из них во внешней функции является излишним. Но нет никакого среднего уровня с существующими цепями инструментов; вам нужно будет написать от руки outer в asm, чтобы воспользоваться знанием того, что ни один из ваших возможных абонентов никогда не захлопывает XMM15, например.


Обратите внимание, что вызывать другие функции MS-ABI изнутри outer() вполне нормально. GCC / clang (за исключением ошибок) также выдаст правильный код для этого, и это нормально, если вызываемая функция решит не уничтожать xmm6..15.

...