Simple question. The function asm
in C is used to do inline assembly in your code. But what does it return? Is it the conventional eax
, and if not, what does it return?
__asm__
itself does not return a value. C standard does not define how __asm__
should handle the return value, so the behavior might be different between compilers. You stated that Visual Studio example is valid, but Visual Studio uses __asm
. __asm__
is used at least by GCC.
To get the result in a C program, you can place return value to eax
in the assembly code, and return from the function. The caller will receive contents of eax
as the return value. This is supported even with optimization enabled, even if the compiler decides to inline the function containing the __asm{}
block.
It avoids a store/reload you'd otherwise get from mov
ing the value to a C variable in the asm and returning that C variable, because MSVC inline asm syntax doesn't support inputs/outputs in registers (except for this return-value case).
Visual Studio 2015 documentation:
int power2( int num, int power )
{
__asm
{
mov eax, num ; Get first argument
mov ecx, power ; Get second argument
shl eax, cl ; EAX = EAX * ( 2 to the power of CL )
}
// Return with result in EAX
// by falling off the end of a non-void function
}
clang -fasm-blocks
supports the same inline-asm syntax but does not support falling off the end of a non-void
function as returning the value that an asm{}
block left in EAX/RAX. Beware of that if porting MSVC inline asm to clang. It will break horribly when compiled with optimization enabled (function inlining).
GCC inline assembly HOWTO does not contain a similar example. You can't use an implicit return as in Visual Studio, but fortunately you don't need to because GNU C inline asm syntax allows specifying outputs in registers. No hack is needed to avoid a store/reload of an output value.
The HOWTO shows that you can store the result to C variable inside the assembly block, and return value of that variable after the assembly block has ended. You can even use "=r"(var)
to let the compiler pick its choice of register, in case EAX isn't the most convenient after inlining.
An example of an (inefficient) string copy function, returning value of dest
:
static inline char * strcpy(char * dest,const char *src)
{
int d0, d1, d2;
__asm__ __volatile__( "1:\tlodsb\n\t"
"stosb\n\t"
"testb %%al,%%al\n\t"
"jne 1b"
: "=&S" (d0), "=&D" (d1), "=&a" (d2)
: "0" (src),"1" (dest)
: "memory");
return dest;
}
(Note that dest
isn't actually an output from the inline asm statement. The matching constraint for the dummy output operands tells the compiler the inline asm destroyed that copy of the variable so it needs to preserve it across the asm statement on its own somehow.)
If you omit a return
statement in a non-void
function with optimization enabled, you get a warning like warning: no return statement in function returning non-void [-Wreturn-type]
and recent GCC/clang won't even emit a ret
; it assumes this path of execution is never taken (because that would be UB). It doesn't matter whether or not the function contained an asm
statement or not.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With