The gcc C compiler has an extension to standard C that allows a programmer to write assembly language instructions within a C function. Of course, you need to be very careful when doing this because you do not know how the compiler has allocated memory and/or registers for the variables. Yes, you can use the “-S” option to see what the compiler did, but if anybody make one change to the function, even compiling it with a different version of gcc, things almost certainly will have changed.
The way to do this is covered in the info pages for gcc. In my version (4.1.2) I found it by going to “C Extensions,” then “Extended Asm.” (No, it’s not obvious to me, either.) The presentation here is a very brief introduction.
The overall format is a C statement of the form:
asm("assembly_language_instruction" : output(s) : inputs(s));
The output operands are destinations for the assembly_language_instruction, and the input operands are sources. Each operand is of the form
"operand_constraint" : C_expression
where the operand_constraint describes what type of register, memory location, etc. should be used for the operand, and C_expression is a C expression, often just a variable name. If there is more than one operand, they are separated by commas.
The assembly_language_instruction can refer to each operand numerically with the “%n” syntax, starting with n = 0 for the first operand, 1 for the second, etc.
For example, let us consider a case where we wish to add two 32-bit integers. (Yes, there is a C operation to do this, but it is generally better to start with simple examples.) The program is shown in Listing D.1.
There is only one output (destination), and its operand constraint is "=m". The ‘=’ sign is required to show that it is an output. The ‘m’ character shows that this operand is located in memory.
Now, recall that the addl instruction requires that at least one of its operands be a register. So we specify the input operand as a register with the "r" operand constraint. We have to do this for the assembly language instruction even though the C code does not specify whether the variable, y, is in memory or in a register.
The operand constraints are described in the info pages for gcc. In my version (4.7.0) I found it by going to “C Extensions,” then “Constraints.” The documentation covers all the architectures supported by gcc, so it is difficult to wade through.
Listing D.2 shows the assembly language actually generated by the compiler.
In fact, the compiler did allocate y in memory, at -4(%rbp). It had to do that because scanf needs an address when reading a value from the keyboard.
The embedded assembly language is between the #APP and #NO_APP comments on lines 35 and 39, respectively.
The movl instruction on line 34 loads y into a register so that the addl instruction on line 37 can add the value to a memory location (x). Of course, it would have had to do that even if we had used a C statement for the addition instead of embedding an assembly language instruction.
There may be situations where you need to use a specific register for a variable. Listing D.3 shows how to do this.
The declaration on line 13,
shows how to request that the compiler use the edx register for the variable z.
We have decided to embed three assembly language instructions. Recall that each assembly language statement is on a separate line. And on the next line, we tab to the place where the operation code begins. In C, the newline character is ’\n’ and the tab character is ’t’. So if you read line 18 carefully, you will see that there are three lines of assembly language. The first one is terminated by a ’\n’. The second instruction begins with a ’\t’ and is terminated by a ’\n’. And the third begins with a ’\t’.
The assembly language results are shown in Listing D.4.
Indeed, we can see our embedded assembly language on lines 37 – 39:
This has been a much abbreviated introduction to embedding assembly language in C. Each situation will be unique, and you will need to study the info pages for gcc in order to determine what needs to be done. You can also expect the rules to change — hopefully become easier to use — as gcc evolves.