Inline assembly for your c programs
Posted by Fotis on June 12, 2006
I got a comment at a previous post about the inline assembly i used and what was that ’strange’ syntax of the asm construct with those ‘: :’ so i decided to make a post about this. Inline assembly is to be used only at special cases e.g. using mmx/sse/sse2 instructions to optimize your code or a special instruction like cpuid to get cpu information.
First of all a small introduction. I’m going to use x86 assembly (another architecture some other time) and the gnu c compiler. It uses the at&t syntax (there is support for intel syntax but it sucks). For all of you who are not familiar with at&t syntax here are some things you must know:
- Registers
The names of all registers begin with %. So if we want to use the eax register we write %eax. - Immediate operants
All immediate operants begin with $. So 0xdeadbeef becomes $0xdeadbeef. - Indirect memory references
Indirect memory references are done using ( ). So if we want the byte pointed by register esi we use (%esi). - Ordering
The source and destination ordering is:
instruction source, destination
It’s the reverse of the intel syntax. - Size of operant
For all instructions that can take operants that have variable sizes you must write this size. The sizes are:- b - Byte (1 byte)
- w - Word (2 bytes)
- l - Long (4 bytes)
For an example showing the above we’re going to move the value 0xcafebabe to the location pointed by %edi. What we’re going to use is:
movl $0xcafebabe, (%edi)
After this introduction we’re getting into the main part of the post, inline assembly. To put inline assembly into one of your programs you must use the “asm” construct. WARNING! This is NOT ansi c! Each compiler uses it’s own constructs and the same compiler may use different ones for different architectures! What i’m going to show you is only for the gnu compiler and for the x86 architecture. The syntax of the “asm” construct is:
asm( assembly code : output operants (optional) : input operants (optional) : globbered registers);
The output operants and input operants consists of a list of the form:
“constraint” (variable), “constraint” (variable), …
At the output operants, constraint shows what is going to be placed at the variable and at the input operants where is the variable going to be placed.
The constraints can take the following values:
- r - any register
- a - eax register
- b - ebx register
- c - ecx register
- d - edx register
- S - esi register
- D - edi register
- f - floating point register
- t - first floating point register
- u - second floating point register
- m - memory operant
- matching operant
Let’s see some examples now.
First a simple one. We want to put the value of variable kot to the register ebx.
asm(”movl %0, %%ebx\n” : : “m” (kot));
%0 gets substituted with the location of kot and then the value is moved to register ebx.
Now let’s put the value of koko to lala. Both of them are at some locations at memory and we can’t use two memory references at the same instruction. So what we are going to do is put the value of koko to eax and then move eax to to lala.
asm(”movl %%eax, %0\n” : “=m” (lala) : “a” (koko));
Since we use the “a” constraint before putting the assembly instructions the compiler first moves the value of koko to eax. Of course this could be done using two instructions.
asm(”movl %1, %%eax\n movl %%eax, %0\n” : “=m” (lala) : “m” (koko) : “%eax” );
You should note that since eax changes value inside the instructions we put it in the list of globbered registers.
Some times we need to use the same register for both input and output. So what we do is use matching constraints. In the following example we are going to increase the value of kot by one.
asm(”incl %0\n” : “=a” (kot) : “0″ (kot));
Some times the compiler while optimizing our code changes the location of the instructions. As an example the compiler could move something we put inside a loop and put it before or after the loop. To instruct the compiler to leave the code to the place we put it we use the volatile keyword. So the above example becomes
asm volatile (”incl %0\n” : “=a” (kot) : “0″ (kot));
Well, this was a simple introduction. I hope you find it useful. Check the GCC-Inline-Assembly-HOWTO for more info.
October 12, 2007 at 2:28 pm
this is very cool n ultimate explanation, thanks !!
December 16, 2007 at 2:33 am
very interesting, but I don’t agree with you
Idetrorce