在c ++代码中使用FPU返回值 [英] Using FPU return values in c++ code
问题描述
我有一个x86 NASM 程序,它似乎工作完美。我有问题使用从它返回的值。这是使用MSVC ++的32位Windows。我希望在 ST0 中返回值。
I have an x86 NASM program which seems to work perfectly. I have problems using the values returned from it. This is 32-Bit Windows using MSVC++. I expect the return value in ST0.
一个最小的例子演示了返回值的问题可以在这个C ++和 NASM 装配代码:
A minimal example demonstrating the problem with the returned values can be seen in this C++ and NASM assembly code:
#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,我希望输出如下:
I can't figure out how to use the return value though, as I always get the wrong value no matter which data type I use. In this example the value 5 should be returned and I'd expect output like:
5.000000
5.000000
5.000000
5
5
而我得到的输出类似于:
Instead I get output similar to:
-9671494178951383518019584.000000
-9671494178951383518019584.000000
-9671494178951383518019584.000000
-9671494178951383518019584.000000
-9.67149e + 24
-9.67149e+24
5
只有最终值显示正确。这个代码有什么问题?为什么不总是返回我期望从我的函数的浮点值?如何修复此代码?
Only the final value appears to be correct. What is wrong with this code? Why doesn't it always return the floating point value I am expecting from my function? How can I fix this code?
推荐答案
主要的问题不是有一个失败返回值在浮点寄存器 ST0 ,但是在尝试从堆栈加载32位(单精度)float参数的方式。问题在这里:
The primary issue is not that there is a failure returning a value in floating point register ST0, but in the way you attempt to load the 32-bit (single precision) float parameter from the stack. The issue is here:
fld dword[esi-8] ;loads the given value into st0
这应该是:
fld dword[esp+4] ;loads the DWORD parameter from stack into st0
fld dword [esi-8]
只能工作,因为调用函数在内部使用 ESI 。使用不同的 C 编译器和优化,您可能会发现代码无法完全工作。
fld dword[esi-8]
only works sometimes because of the way the calling function uses ESI internally. With different C compilers and optimizations enabled you may find the code fails to work altogether.
使用32位C / C ++代码参数在堆栈上从右到左。当在32位代码中执行 CALL 指令时,4字节返回地址被放置在堆栈上。内存地址 esp + 0
将包含返回地址,第一个参数将位于 esp + 4
。如果你有第二个参数,它将在 esp + 8
。在此 CDECL 调用约定的详细说明=nofollow> WikiBook条目。重要性:
With 32-bit C/C++ code parameters are passed on the stack from right to left. When you do a CALL instruction in 32-bit code the 4 byte return address is placed on the stack. Memory address esp+0
would contain the return address and the first parameter would be at esp+4
. If you had a second parameter it would be at esp+8
. A good description of the Microsoft 32-bit CDECL calling convention can be found in this WikiBook entry. Of importance:
- 函数参数以从右到左的顺序传递到堆栈。 li>
- 函数结果存储在EAX / AX / AL
- 浮点返回值将在ST0中返回
- 8位和16位整数参数提升为32位参数。
对于x87 FPU指令,非常重要的是,当返回 FLOAT 时,堆栈上的唯一值是 ST0 中的值。不能释放(弹出/释放)任何其他你放在FPU堆栈可能导致您的函数失败时调用多次。 x87 FPU堆栈只有8个插槽(不是很多)。如果在函数返回之前不清除FPU堆栈,当未来指令需要在FPU堆栈上加载新值时,可能导致FPU堆栈溢出。
When dealing with x87 FPU instructions it is very important that the only value on the stack when returning a FLOAT is the value in ST0. Failure to release(popping/freeing) anything else you put on the FPU stack can lead to your function failing when called multiple times. The x87 FPU stack only has 8 slots (not very many). If you don't clean off the FPU stack before the function returns, can lead to FPU stack overflows when future instructions need to load a new value on the FPU stack.
您的函数的示例实现可能如下所示:
An example implementation of your function could have looked like:
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)
ret
这篇关于在c ++代码中使用FPU返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!