Используйте соглашение о вызовах 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.