为什么不在XMM向量寄存器中存储函数参数?

问题描述 投票:14回答:2

我正在阅读这本书:“计算机系统 - 程序员视角”。我发现,在x86-64架构中,我们仅限于6个积分参数,这些参数将被传递给寄存器中的函数。下一个参数将在堆栈上传递。

而且,第一个最多8个FP或矢量args以xmm0..7传递。

为什么不使用浮点寄存器来存储下一个参数,即使参数不是单/双精度变量?

将数据存储在寄存器中比将其存储到存储器然后从存储器中读取它会更有效(据我所知)。

assembly x86 parameter-passing x86-64 calling-convention
2个回答
20
投票

大多数函数没有超过6个整数参数,所以这实际上是一个极端情况。在xmm寄存器中传递一些超出的整数参数将使得找到浮点算法的位置的规则更加复杂,几乎没有任何好处。除了它可能不会使代码更快的事实。

将多余参数存储在内存中的另一个原因是您可能无法立即使用它们。如果要调用另一个函数,则必须将这些参数从xmm寄存器保存到内存,因为您调用的函数将破坏任何参数传递寄存器。 (并且所有的xmm regs都是调用者保存的。)所以你可能最终得到的代码将参数填充到向量寄存器中,它们不能直接使用,并且在调用另一个函数之前将它们存储到内存中,并且只有然后将它们加载回整数寄存器。或者即使该函数没有调用其他函数,也许它需要向量寄存器供自己使用,并且必须将params存储到内存中以释放它们以运行向量代码!将push params放到堆栈上会更容易,因为push非常优化,出于显而易见的原因,在一个uop中进行存储和修改RSP,大约和mov一样便宜。

有一个整数寄存器不用于参数传递,但在SysV Linux/Mac x86-64 ABI(r11)中也没有调用保存。在没有保存的情况下使用临时动态链接器代码的临时寄存器很有用(因为这样的填充函数需要将所有的args传递给动态加载的函数),以及类似的包装器函数。

因此,AMD64可能会使用更多的整数寄存器作为函数参数,但这只会以调用函数在使用前必须保存的寄存器数量为代价。 (或者两用r10用于不使用“静态链”指针的语言。)

无论如何,在寄存器中传递的更多参数并不总是更好。


xmm寄存器不能用作指针或索引寄存器,将xmm寄存器中的数据移回整数寄存器可能会比加载刚刚存储的数据更慢。 (如果任何执行资源将成为瓶颈,而不是缓存未命中或分支错误预测,则更有可能是ALU执行单元,而不是加载/存储单元。将数据从xmm移动到gp寄存器需要ALU uop,在Intel中和AMD目前的设计。)

L1高速缓存非常快,而存储 - >负载转发使得往返内存的总延迟达到例如5个周期。英特尔Haswell。 (像inc dword [mem]这样的指令的延迟是6个周期,包括一个ALU周期。)

如果将数据从xmm移动到gp寄存器就是你要做的事情(没有其他任何东西可以保持ALU执行单元忙),那么是的,在Intel CPU上,movd xmm0, eax / movd eax, xmm0的往返延迟(2个周期Intel Haswell)更少比mov [mem], eax / mov eax, [mem]的延迟(5个周期Intel Haswell),但整数代码通常不会像FP代码那样通过延迟完全陷入瓶颈。

在AMD Bulldozer系列CPU中,两个整数内核共享一个向量/ FP单元,直接在GP regs和向量regs之间移动数据实际上非常慢(单向8或10个周期,或者是Steamroller的一半)。内存往返仅为8个周期。

32位代码设法运行得相当好,即使所有参数都在堆栈上传递,并且必须加载。 CPU经过高度优化,可以将参数存储到堆栈中,然后再次加载它们,因为古老的32位ABI仍然用于大量代码,尤其是。在Windows上。 (大多数Linux系统大多运行64位代码,而大多数Windows桌面系统运行大量32位代码,因为许多Windows程序仅作为预编译的32位二进制文​​件提供。)

有关CPU微体系结构指南,请参阅http://agner.org/optimize/,了解如何确定实际需要多少循环。 wiki中还有其他很好的链接,包括上面链接的x86-64 ABI文档。


3
投票

我认为这不是一个好主意,因为:

  1. 您不能将FPU / SSE寄存器用作通用寄存器。我的意思是,这段代码不正确(NASM): mov byte[st0], 0xFF
  2. 如果比较向/从FPU / SSE发送数据与通用寄存器/存储器,FPU / SSE非常慢。

编辑:记住,我可能不对。

© www.soinside.com 2019 - 2024. All rights reserved.