当前位置: 首页 > 知识库问答 >
问题:

解决windows调用约定保留xmm寄存器的问题?

阚元白
2023-03-14

Windows上是否有任何方法可以解决XMM寄存器保留在函数调用中的要求?(除了将其全部写入汇编中)

不幸的是,我有许多AVX2内在函数因此而臃肿。

例如,这将被编译器(MSVC)放置在函数的顶部:

00007FF9D0EBC602 vmovaps xmmword ptr[rsp 1490h]、xmm6
00007FF9 D0EBC60B vmovaps XMMWORDPTR[rsp 1480h]、xmm7
000007FF9 D0 EBC614 VMOVAPsXMMWOORD ptr[rsp 1470h],xmm8
0000 7FF9 0EBC61D VMOVAPSXMMWOrdPTR[Rp 1460h]、xmm9
0 07FF9 EBC626 VMOVAP XMMWORTR[rsp1450H];xmm10
EBC62F vmovaps xmmword ptr[rsp 1440h]、xmm11
00007FF9D0EBC638 vmovaps XMMWORDPTR[rsp 1430h],xmm12
00007FF9D0EBC641 vmovaps xmmword ptr[rsp 1420h]、xmm13
00007FF9 D0EBC64A vmovaps XMMWOORD ptr[rsp 1410h]、xmm14
000007FF9 D0 EBC653 VMOVAP XMMWORDPTR[rsp 1400h]和xmm15

然后在函数的末尾。。

00007FF9D0EBD6E6 vmovaps xmm6,xmmword ptr[r11-10h]<br>00007FF9 EBD6EC vmovaps xmm7,xmmword ptr[r11-20h]<br>00007 FF9D0eBD6F2 VMOVAP xmm8,xmmword ptr[R1 1-30h]<<br>000007FF9 EBD 6F8 VMOVOPS xmm9,XMMWARD ptr[r11-40h]<>br>000007 FF9 EBD0D6FE VMOVAPSXMM10,XMMWD ptr[R 11-50h]<00007FF9D0EBD704VMOVAPS xmm11、xmmword ptr[r11-60h]<br>00007FF9 D0EBD70 A vmovaps xmm12,xmmword ptr[r11-70h]<br>00007FF9D0EBD710 vmovaps xmm13,XMMWORDPTR[r11-80h]<br>00007 FF9D0eBD716 vmovaps xmm14,XMMwordPTR[l11-90h]<>00007ff9D0EBD 71F VMOVAP xmm15,XMMWordPTR[r11-0A0h]

这20条指令没有任何作用,因为我不需要维护XMM国。我有100个这样的函数,编译器会像这样膨胀。它们都是通过函数指针从同一个调用点调用的。

我尝试更改调用约定(__vectorcall/cdecl/fastcall),但似乎没有任何作用。

共有1个答案

景恩
2023-03-14

对您想要通过函数指针拼凑的辅助函数使用x86-64 System V调用约定。在该调用约定中,所有xmm/ymm0...15和zmm0...31都是调用失败的,因此即使需要超过5个向量寄存器的辅助函数也不必保存/恢复任何向量寄存器。

调用它们的外部解释器函数应该仍然使用Windows x64 fastcall或vectorcall,所以从外部来看,它完全遵守那个调用约定。

这将把XMM6..15的所有保存/恢复提升到该调用程序中,而不是提升每个helper函数。这减少了静态代码大小,并通过函数指针分摊了多次调用的运行时成本。

AFAIK,MSVC不支持使用x86-64 System V调用约定的标记函数,只有fastcall与vectorcall,因此您必须使用clang。

(ICC有错误,无法在调用System V ABI函数时保存/恢复XMM6...15)。

Windows GCC在溢出< code>__m256的32字节堆栈对齐方面存在缺陷,因此通常情况下,将GCC与包含AVX的< code>-march=一起使用是不安全的。

对函数和函数指针声明使用 __attribute__((sysv_abi))__attribute__((ms_abi))。

我认为< code>ms_abi是< code>__fastcall,而不是< code>__vectorcall。Clang可能也支持< code > _ _ attribute _ _((vector call)),但是我没有试过。谷歌结果大多是功能请求/讨论。

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上用clang 8.0 < code >-O3-March = sky lake 编译如下。(Godbolt上的gcc/clang以Linux为目标,但是我在函数和函数指针上都使用了显式的< code>ms_abi和< code>sysv_abi,所以代码生成不依赖于缺省值为< code>sysv_abi的事实。显然,您希望用Windows gcc或clang来构建您的函数,这样对其他函数的调用将使用正确的调用约定。和有用的目标文件格式等。)

注意,gcc/clang为< code>outer()发出代码,该代码在RCX (Windows x64)中期待传入指针arg,但在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.这是一个阻碍者的错误;如果任何被调用方确实像允许的那样关闭这些regs,那么从此函数返回将违反其调用约定。

这使得clang成为您唯一可以使用的编译器。没关系;clang非常好。

如果您的被调用者不需要所有的YMM寄存器,那么在外部函数中保存/恢复所有的寄存器是多余的。但是现有的工具链没有中间地带;例如,您必须在asm中手写< code>outer,以充分利用您所知道的任何可能的被调用者都不会破坏XMM15的优势。

请注意,从<code>外部()内部调用其他MS-ABI函数是完全可以的。GCC/clang也会(排除bug)为此发出正确的代码,如果被调用的函数选择不破坏xmm6..15,这也没关系。

 类似资料:
  • 我相信我了解linux x86-64 ABI如何使用寄存器和堆栈将参数传递给函数(参见前面的ABI讨论)。我感到困惑的是,在函数调用中是否/哪些寄存器应该保留。也就是说,哪些寄存器被保证不被破坏?

  • 从Intel在https://software.intel.com/en-us/articles/introduction-to-x64-assembly上介绍x64汇编, RCX、RDX、R8、R9用于整数和指针参数,从左到右顺序为 寄存器RAX、RCX、RDX、R8、R9、R10和R11被认为是易失的,必须在函数调用时被销毁。 RBX、RBP、RDI、RSI、R12、R14、R14和R15必须

  • 因此,每个测试都有3+1=4个周期的延迟。 其中一些可以通过在、等之间交替并行运行。 但它仍然相当慢。 有没有更快的方法来实现这一点? 我需要在一行中测试8个XMM/YMM寄存器。一字节位图中每个寄存器1位。

  • 在wikipedia x86调用约定中,它说对于Microsoft x64调用约定: 寄存器RBX、RBP、RDI、RSI、RSP、R12、R13、R14和R15被视为非易失性(被叫方保存)。 但对于System V AMD64 ABI: 如果被调用方希望使用寄存器RBX、RBP和R12-R15,则必须在将控制权返回给调用方之前恢复它们的原始值。 我的问题是,在不同的平台上调用约定是不是不同的?(

  • 正如在另一个问题中给我的建议,我检查了windows ABI,但如果我自己不调用windows API,我会对我能做什么和不能做什么感到有点困惑。 我的场景是,我正在编程.NET,需要在asm中针对特定处理器的一小块代码,用于对数组进行大量多遍处理的时间关键代码段。 在https://msdn.microsoft.com/en-us/library/9z1stfyw.aspx检查ABI中的注册信息

  • 本文向大家介绍解决pytorch 保存模型遇到的问题,包括了解决pytorch 保存模型遇到的问题的使用技巧和注意事项,需要的朋友参考一下 今天用pytorch保存模型时遇到bug Can't pickle <class 'torch._C._VariableFunctions'> 在google上查找原因,发现是保存时保存了整个模型的原因,而模型中有一些自定义的参数 将 torch.save(m