我有一个x86的NASM程序,这似乎是完美的工作。我在使用从中返回的值时遇到了问题。这是使用MSVC++的32位窗口.我期望ST0中的返回值。
在这个C++和NASM程序集代码中可以看到一个演示返回值问题的最小示例:
#include <iostream>
extern "C" float arsinh(float);
int main()
{
float test = arsinh(5.0);
printf("%f\n", test);
printf("%f\n", arsinh(5.0));
std::cout << test << std::endl;
std::cout << arsinh(5.0) << std::endl;
}装配代码:
section .data
value: dq 1.0
section .text
global _arsinh
_arsinh:
fld dword[esi-8] ;loads the given value into st0
ret但是,我不知道如何使用返回值,因为无论使用哪种数据类型,我总是得到错误的值。在本例中,应该返回值5,我希望输出如下:
5.000000 5.000000 5 5
相反,我得到的输出类似于:
-9671494178951383519584.000000 -9671494178951383519584.000000 -9.67149e+24 5
只有最终值似乎是正确的。这个代码有什么问题?为什么它不总是从函数中返回我期望的浮点值?我怎样才能修正这段代码?
发布于 2016-06-19 06:54:08
主要问题不是在浮点寄存器ST0中返回值的失败,而是尝试从堆栈加载32位(单精度)浮动参数的方式。问题是:
fld dword[esi-8] ;loads the given value into st0应改为:
fld dword[esp+4] ;loads the DWORD parameter from stack into st0fld dword[esi-8]有时只是因为调用函数内部使用ESI的方式而起作用。如果启用了不同的C编译器和优化,您可能会发现代码完全无法工作。
使用32位C/C++,代码参数从右到左在堆栈上传递。当您在32位代码中执行调用指令时,4字节返回地址将放置在堆栈上。内存地址esp+0将包含返回地址,第一个参数将位于esp+4。如果您有第二个参数,那么它将位于esp+8。在此WikiBook条目中可以找到对Microsoft32位CDECL调用约定的良好描述。具有重要意义:
在处理x87 FPU指令时,非常重要的是,当返回浮点时,堆栈上唯一的值是ST0中的值。未能释放(弹出/释放)您放置在FPU堆栈上的任何其他内容,都可能导致功能多次调用失败。x87 FPU堆栈只有8个槽(不是很多)。如果在函数返回之前不清除FPU堆栈,则可能导致FPU堆栈在以后的指令需要在FPU堆栈上加载新值时溢出。
您的函数的一个示例实现可能如下所示:
use32
section .text
; _arsinh takes a single float (angle) as a parameter
; angle is at memory location esp+4 on the stack
; arcsinh(x) = ln(x + sqrt(x^2+1))
global _arsinh
_arsinh:
fldln2 ; st(0) = ln2
fld dword[esp+4] ; st(0) = angle, st(1)=ln2
fld st0 ; st(0) = angle, st(1) = angle, st(2)=ln2
fmul st0 ; st(0) = angle^2, st(1) = angle, st(2)=ln2
fld1 ; st(0) = 1, st(1) = angle^2, st(2) = angle, st(3)=ln2
faddp ; st(0) = 1 + angle^2, st(1) = angle, st(2)=ln2
fsqrt ; st(0) = sqrt(1 + angle^2), st(1) = angle, st(2)=ln2
faddp ; st(0) = sqrt(1 + angle^2) + angle, st(1)=ln2
fyl2x ; st(0) = log2(sqrt(1 + angle^2) + angle)*ln2
; st(0) = asinh(angle)
rethttps://stackoverflow.com/questions/37865951
复制相似问题