Assembly 推送到浮点寄存器而不是堆栈

Assembly 推送到浮点寄存器而不是堆栈,assembly,cpu-registers,Assembly,Cpu Registers,我有一个需要尽可能快的函数,它只使用整数运算。它在AMD64体系结构上运行,我需要进行一些push/pop操作,以便有足够的寄存器可以使用。现在我想知道,x64 ABI声明前四个浮点寄存器(XMM0、XMM1、XMM2和XMM3)是易失性的,不需要跨函数调用保留 因此,我想我可以通过movq(MMX或SSE指令集)将需要保留的64位寄存器存储在这些寄存器的低64位(即MM0、MM1等),而不是使用堆栈,从而节省一些内存加载/存储。此外,我不需要使用EMM存储FPU状态—这将无法达到目的—因为我实

我有一个需要尽可能快的函数,它只使用整数运算。它在AMD64体系结构上运行,我需要进行一些push/pop操作,以便有足够的寄存器可以使用。现在我想知道,x64 ABI声明前四个浮点寄存器(XMM0、XMM1、XMM2和XMM3)是易失性的,不需要跨函数调用保留

因此,我想我可以通过movq(MMX或SSE指令集)将需要保留的64位寄存器存储在这些寄存器的低64位(即MM0、MM1等),而不是使用堆栈,从而节省一些内存加载/存储。此外,我不需要使用EMM存储FPU状态—这将无法达到目的—因为我实际上并没有操作浮点寄存器,而只是将它们用作存储(无论如何,x64下几乎不使用x87单元,因为它基本上被SSE取代)

我已经做了修改,效果很好(没有崩溃,性能提高了4%),但我想知道,这个“黑客”真的有效吗,或者它会带来我可能忽略的任何特定副作用(比如FPU状态腐败,即使我不使用它,诸如此类的事情)。在任何当前架构上,加载/存储到FPU寄存器的速度是否总是比内存加载/存储快

是的,这种优化是非常必要的。平心而论,这不会严重降低代码维护成本,一行注释就足以解释这个技巧。因此,如果我可以免费获得每个字节少几个时钟,而不会产生意外的后果,我将很乐意接受它们:)


谢谢。

只有在MMX操作后清除状态时才需要EMMS指令。SSE指令不需要它。所以这肯定不会有冲突

当然,您应该记住,不同的编译器和操作系统使用不同的调用约定,有些编译器和操作系统可能会对这四个寄存器进行不同的处理

然而,只要记住这一点,我认为这种方法没有问题。根据ABI,所有寄存器的使用方式都是应该的


假设这是用汇编编写的,不需要考虑这是否会阻碍编译器优化(一个C++的函数,它进入ASM并开始讨论特定的寄存器,使得编译器优化代码变得困难得多)

我认为64位只有一个调用约定。(大概是为了提高兼容性),但我将再次进行更深入的检查。事实上,如果不同的约定不将前四个SSE寄存器视为易失性寄存器,这种方法将被打破。@遗憾的是,Thomas Windows使用的约定与Linux不同。我不知道它们为什么会出现分歧,老实说,微软选择使用的那个看起来非常愚蠢(实际上永远不会在XMM寄存器中传递SIMD值。总是将它们推到堆栈上)是的,它是作为一个纯汇编子程序编写的,因此编译器不需要查看它,可以将其视为一个黑匣子。这也许可以解释为什么我的代码在Ubuntu下失败,那么(这只是一个快速而肮脏的测试,我还不打算对此进行研究)。希望这种方法仍然有效(可能需要一些最小的平台特定调整)。遗憾的是,Windows和Linux永远无法完全一致,这使得交叉编译变得极其乏味。@jalf是在MSVC2013中引入的,目的是修复其中的一些缺陷。如果您将其存储在MM寄存器中,为什么前4个XMM寄存器是否易失性会很重要?@harold:因为否则我必须推送并弹出XMM寄存器才能避免viol吃了ABI,这首先会破坏避免内存加载/存储的目的。我认为。在任何情况下都不需要这样做-MM寄存器不会与XMM寄存器重叠。@harold所以我应该使用XMM寄存器,让FPU单独使用,对吗?我会这样做。谢谢。真的,我不需要这样做用16个XMM寄存器推到堆栈!实际上使用XMM寄存器没有任何好处,因为它们已经被被调用者推送/弹出(不幸的是),所以看起来我似乎被MM寄存器卡住了。不过应该这样做。