Search code examples
c++linuxassemblyx86-64calling-convention

Trouble returning float value from fpatan assembler function back into c++ program


I have a small program written in AT&T assembler for the x64 processor in linux and compiled it using Gnu Assembler using the fpatan function. I know I can get the atan value other ways. I am not able to understand how to move floating point values created in the st0-st7 registers into a C++ program.

In this example, I can view the result value using GDB.

My code is as follows:

# trigtest1.s - An example of using the FPACOS instructions
.section .data
y: 
    .float 1.625
x: 
    .float .25

.section .bss
   .lcomm result, 4

.section .text
.globl _start
_start:
   nop
   finit
   flds y
   flds x
   fpatan
   fsts result

   movl $1, %eax
   movl $0, %ebx
   int $0x80

I can compile and link it with:

as -gstabs trigtest1.s -o trigtest.o ld trigtest.o -o trigtest

With GDB after stepping through the program with a breakpoint set at movl $1, %eax I get: (gdb)x/f &result 0x6000e0 : 1.41814697

This is what I was expecting. The problem comes when I try to use it in a C++ program. First here is the function.

# asm.s - An example of using the FPATAN function
.global ArcTan
.section .data
y: 
    .float 1.625
x: 
    .float .25

.section .bss
   .lcomm result, 4

.section .text
ArcTan:
   nop
   finit
   flds y
   flds x
   fpatan
   fsts result
   nop

   ret

And here is the C++ program that calls the assembler function:

#include <iostream>

using namespace std;

extern "C" float ArcTan();

int main()
{

cout<<"Arctan is "<<ArcTan()<<endl;

cout<<endl;

return 0;
}

I compile and link using: as -gstabs asm.s -o asm.o g++ -gstabs -O0 main.cpp asm.o -o runme

When I use GDB to view what is going on, the value in result is never updated and stays at 0. I would like to use the value calculated with fpatan in the C++ program but have not been successful in returning the value. Any help will be greatly appreciated.


Solution

  • x86-64 returns float in XMM0, not legacy x87 st0. Build your code with
    g++ -m32 asm.s main.cpp -o runme to build 32-bit code that uses the 32-bit calling convention if you want to use legacy x87 for FP math.

    Note that your C++ code isn't reading result, it's reading the return value of the function which it expects in a register. If you did use extern float result; in your C++ code, you could read it just fine.

    (turns out Michael Petch has already written a full answer on Return a float from a 64-bit assembly function that uses x87 FPU, closing this as a duplicate.)