我有一个用MASM 64/ML 64汇编的X64 ASM例程。它是一个独立的叶函数,***不是***内联汇编。它用在Visual Studio解决方案中的C/C++程序中。
我在MSDN上找到了两篇关于保留寄存器的参考文献:
第一个是用于内联汇编的,但它特别声明在使用__fastcall
时要保留ECX。它似乎也缺少对X64的处理,因为它引用了32位寄存器。
第二条告诉我们 “RAX、RCX、RDX、R8、R9、R10、R11被认为是易失性的,必须在函数调用时被认为是被销毁的”。不幸的是,它没有 * 清楚地 * 说明它们是否需要被保留。(如果你仔细看,它使用了误导,而不是说明要采取的操作)。
我认为第二篇文章在这种情况下是控制性的,但我想澄清一下,以免混淆......是否需要为X64快速调用叶函数保留CX/ECX/RCX?
2条答案
按热度按时间wz1wpwve1#
"Using and Preserving Registers in Inline Assembly"文章仅讨论x86,不适用于x86-64。
"Caller/Callee Saved Registers"文章是关于x86-64调用约定的,并明确指出RCX寄存器是易失性的,因此不需要被调用方保存。
@rkhb的一条评论提到"Overview of x64 Calling Conventions"文章是混淆的来源,大概是因为它说:
x64只使用
__fastcall
调用约定和基于RISC的异常处理模型但是,如果您跟随引用中的
__fastcall
链接,您将看到它说,“此调用约定[__fastcall
]仅适用于x86体系结构”。我认为概述文章的真正意思是说,“x64使用类似于__fastcall
的调用约定,其中使用寄存器来传递参数”。wvmv3b1j2#
因为调用者必须假设函数销毁了这些寄存器,所以被调用者 * 被 * 允许销毁它们而不保留它们。也就是说,这些寄存器是volatile, aka call-clobbered。这适用于整个64位寄存器列表,包括所有传递参数的寄存器。
与函数内周围C/C++代码的内联
__asm
接口本质上与函数之间的ABI /调用约定无关。例如x86(32位)fastcall具有调用损坏的ECX,就像参数传递寄存器一样。无论如何,32位fastcall是与Windowsx 64 fastcall不同的调用约定,因此您所读到的关于32位fastcall的内容对于64位并不具有权威性。MSVC内联asm支持是如此脆弱,并且在其编译器中实现得非常笨拙,以至于他们需要您的asm来跳过带有寄存器参数的函数中的循环,因为它们的编译器太笨了,无法像处理普通变量那样将它们保存在安全的地方。例如,the doc you linked会说“* 这会在具有__asm块的函数中产生问题,因为函数无法分辨哪个参数在哪个寄存器中。*”但这显然是毫无意义的,因为调用约定已经确定了这一点,否则编译器就不能让asm在不使用内联asm的函数中访问它的参数。(或者,这是对编译器中的 inline asm 代码不是为处理寄存器参数调用约定而编写的,并且从未被重写这一事实的拙劣描述。)其他编译器,包括
clang-cl
,我没有这个问题。这就是所有关于保留ECX的废话的来源,与函数在asm中相互调用的方式无关,如果使用MSVC的脆弱内联asm,则只需要函数内部的要求。