【发布时间】:2018-03-31 16:45:29
【问题描述】:
如何将浮点数作为函数参数(外部调用)传递并在内联汇编中返回浮点数?下面的示例不起作用并且正在使应用程序崩溃。 cmets 是我的,所以它们也可能是错误的。
前两行是我自己添加的,只是为了这个例子。最初我从 st(0) 和 st(1) 上的两个浮点值开始,对此我无能为力。
fld a ; load float 'a' on st(0)
fld b ; load float 'b' on st(0), 'a' is now st(1)
sub esp, 4 ; make room for float
fstp dword ptr [esp] ; push st(0) on stack, pop st(0)
mov ecx, ebp ; move 'this' on ecx
call Class::ModValue ; returns float on st(0)
fcompp ; compare returned st(0) with st(1)
fnstsw ax
test ah, 41h
jnz Exit_label
上面的代码 sn-p 在asm{} 块内,前后还有更多不重要的代码。崩溃发生在此代码片段的第一行和ModValue 函数调用之间。
函数签名:
float Class::ModValue(float value)
{
_LOG("ModValue") // doesn't show
return value;
}
编译器:VisualStudio,架构:x86,调用约定:__thiscall
【问题讨论】:
-
IIRC,
__thiscall就像大多数 32 位调用约定一样,并在内存中传递 FP 参数(在 ESP 之上的同一个地方,你可以找到整数。调用堆栈,而不是 x87 寄存器堆)。 FP 值在st(0)中返回。 -
这不是minimal reproducible example。这个 inline 程序集是在 C++ 中的
asm{}块内,还是在单独的 .asm 文件中?如果是后者,你怎么称呼它,显然它会崩溃,因为你的函数末尾没有ret。崩溃如何,在哪个指令上?使用你的调试器。 -
@PeterCordes 感谢您的建议,用更多信息编辑了问题。它在 C++ 中的
asm{}块内 -
显示minimal reproducible example 的周围代码,以及
Class::ModValue的原型。我认为__thiscall是一个调用者弹出约定,在这种情况下,可以通过修改 ESP 的 inline-asm 块来解释崩溃。在函数调用后使用pop eax从堆栈中删除参数。与其他寄存器不同(MSVC 在您破坏时会注意到),我认为 MSVC 假设您不修改 ESP。 -
此外,如果您的代码将在本世纪制造的 CPU 上运行,您可以使用
fcomip直接比较 EFLAGS。 (PPro 中的新功能)。不过,它只会弹出一次 x87 堆栈。此外,您真的需要使用旧版 x87 而不是 SSE 吗?通常您可以假设至少 SSE2 (Pentium4 / Athlon64),几乎可以肯定 SSE1 仅用于单精度 (Pentium III / Athlon XP)
标签: c++ assembly x86 inline-assembly calling-convention