views:

300

answers:

7

Hi awesomes~

Currently I am writing some assembly language procedures. As some convention says, when I want to return some value to the caller, say an integer, I should return it in the EAX register. Now I am wondering what if I want to return a float, a double, an enum, or even a complex struct. How to return these type of values?

I can think of returning an address in the EAX which points to the real value in memory. But is it the standard way?

Many thanks~~~

A: 

Typically you would use the stack

davydotcom
Using the C ABI you use the stack. In C++ it is undefined thus allowing compiler manufacturers to choose more effecient mechanisms (if they want).
Martin York
A: 

If you're planning to interface with C or another higher-level language, typically you would accept the address of a memory buffer as an argument to your function and return your complex value by populating that buffer. If this is assembly-only, then you can define your own convention using any set of registers you want, although usually you'd only do so if you have a specific reason (e.g., performance).

llasram
+9  A: 

It is all up to you, if the caller is your code. If the caller is not under your control, you have to either follow their existing convention or develop your own convention together.

For example, on x86 platform when floating-point arithmetic is processed by FPU instructions, the result of a function is returned as the top value on the FPU register stack. (If you know, x86 FPU registers are organized into a "circular stack" of sorts). At that moment it is neither float nor double, it is a value stored with internal FPU precision (which could be higher than float or double) and it is the caller's responsibility to retrieve that value from the top of FPU stack and convert it to whatever type it desires. In fact, that is how a typical FPU instruction works: it takes its arguments from the top of FPU stack and pushes the result back onto FPU stack. By implementing your function in the same way you essentially emulate a "complex" FPU instruction with your function - a rather natural way to do it.

When floating-point arithmetic is processed by SSE instructions, you can choose some SSE register for the same purpose (use xmm0 just like you use EAX for integers).

For complex structures (i.e. ones that are larger than a register or a pair of registers), the caller would normally pass a pointer to a reserved buffer to the function. And the function would put the result into the buffer. In other words, under the hood, functions never really "return" large objects, but rather construct them in a caller-provided memory buffer.

Of course, you can use this "memory buffer" method for returning values of any type, but with smaller values, i.e. values of scalar type, it is much more efficient to use registers than a memory location. This applies, BTW, to small structures as well.

Enums are usually just a conceptual wrapper over some integer type. So, there's no difference between returning a enum or an integer.

AndreyT
+5  A: 

A double should be returned as the 1st item in the stack.

Here is a C++ code example (x86):

double sqrt(double n)
{
    _asm fld n
    _asm fsqrt
}  

If you prefer to manage the stack manually (saving some CPU cycles):

double inline __declspec (naked) __fastcall sqrt(double n)
{
    _asm fld qword ptr [esp+4]
    _asm fsqrt
    _asm ret 8
}

For complex types, you should pass a pointer, or return a pointer.

Lior Kogan
If you say that you also need to specify the compiler (wih version) and OS (with version) though you should probably specify the ABI document for your compiler. This is because not all C++ compilers do that. All C compiler will because the C ABI is well defined.
Martin York
Right. it is for Win32, VC++ 2008. But will probably work for all VC versions.
Lior Kogan
A: 

C99 has a complex builtin data type (_Complex). So if you have a C99 compliant compiler, you could just compile some function that returns a complex and compile this to assembler (usually with a -S option). There you can see the convention that is taken.

Jens Gustedt
also try with `-save-temps`.
Matt Joiner
+2  A: 

When you have questions about calling conventions or assembly language, write a simple function in high level language (in a separate file). Next, have your compiler generate an assembly language listing or have your debugger display "interleaved assembly".

Not only will the listing tell you how the compiler implements code, but also show you the calling conventions. A lot easier than posting to S.O. and usually faster. ;-)

Thomas Matthews
A: 

It depends on the ABI. For example, Linux on x86 uses the Sys V ABI, specified in the Intel386 Architecture Processor Supplment, Fourth Edition.

The section Function Calling Sequence section has the information on how values are to be returned. Briefly, in this API:

  • Functions returning scalars or no value use %eax;
  • Functions returning floating point values use %st(0);
  • For functions returning struct or union types, the caller provides space for the return value and passes its address as a hidden, first argument. The callee returns this address in %eax.
caf