We can instruct the compiler to insert the code of a function into the code of its callers, to the point where actually the call is to be made. Such functions are inline functions. Sounds similar to a Macro? Indeed there are similarities.
What is the benefit of inline functions?
This method of inlining reduces the function-call overhead. And if any of the actual argument values are constant, their known values may permit simplifications at compile time so that not all of the inline function’s code needs to be included. The effect on code size is less predictable, it depends on the particular case. To declare an inline function, we’ve to use the keyword
inline
in its declaration.
Inline assembly is important primarily because of its ability to operate and make its output visible on C variables. Because of this capability, "asm" works as an interface between the assembly instructions and the "C" program that contains it.
Programming Languages |
movl %ecx, %ebxNotice the 'l' at the end of mov. This specifies that the instruction is working on 32 bit operands. 'w' indicates that the instruction is using 16 bit operands and 'b' for 8 bit.
So, with all that under your belt, how do you actually add it into your code? You use the asm keyword. It takes the following form.
asm("instructions" : outputs : inputs : clobber list);You don't actually need to use the last three, but for longer code you will need them. Let's see what they do.
asm volatile(" pushl %%eax movl %1, %%eax movl %2, %%ebx addl %%ebx, %%eax movl %%eax, %0 popl %%eax" : "=g" (i) : "g" (j), "g" (k) : "bx" );Wow. Let's go through that piece of code step by step. The actual code, as you can probably figure out, adds j and k and puts the output in i. Firstly, what's with the '%%'? If you have any inputs or outputs, then you must put %% before your register names. Next up, the input list. Who is 'g'? G simply tells the compiler to put the argument anywhere. You can then reference them in order, %0 is i, %1 is j and %2 is k. '=g' tells the compiler that it is output. We put ebx into the clobbered list because it gets clobbered.
Clobber List
Some instructions clobber some hardware registers. We have to list those registers in the clobber-list, ie the field after the third ’:’ in the asm function. This is to inform gcc that we will use and modify them ourselves. So gcc will not assume that the values it loads into these registers will be valid. We shoudn’t list the input and output registers in this list. Because, gcc knows that "asm" uses them (because they are specified explicitly as constraints). If the instructions use any other registers, implicitly or explicitly (and the registers are not present either in input or in the output constraint list), then those registers have to be specified in the clobbered list.
Volatile ...?
If you are familiar with kernel sources or some beautiful code like that, you must have seen many functions declared as
volatile
or __volatile__
which follows an asm
or __asm__
. I mentioned earlier about the keywords asm
and __asm__
. So what is this volatile
?
If our assembly statement must execute where we put it, (i.e. must not be moved out of a loop as an optimization), put the keyword
volatile
after asm and before the ()’s. So to keep it from moving, deleting and all, we declare it asasm volatile ( ... : ... : ... : ...);
Use
__volatile__
when we have to be verymuch careful.
If our assembly is just for doing some calculations and doesn’t have any side effects, it’s better not to use the keyword
volatile
. Avoiding it helps gcc in optimizing the code and making it more beautiful.
Example C Program:
//make value of b equal to value of a
#include<stdio.h>
int main(int argc,char *argv)
{
int a=10, b;
asm volatile("movl %1, %%eax;movl %%eax, %0;":"=r"(b):"r"(a) :"%eax" );
printf("%d\n",b);
}