Inline Assembly: Difference between revisions

From OSDev.wiki
Jump to navigation Jump to search
[unchecked revision][unchecked revision]
Content added Content deleted
m (Reverted edits by Nekros (Talk); changed back to last version by Walling)
(Undo revision 7409 by Nekros (Talk))
Line 44: Line 44:


These labels are in a namespace of their own, and will not collide with any C identifiers. The same can be done for input operands, too.
These labels are in a namespace of their own, and will not collide with any C identifiers. The same can be done for input operands, too.
Input Operands
==Input Operands==


The Input Operands allow to parameterize the ASM code; i.e., passing parameters from C code to ASM block. Again, string literals are used to specify the details.
The Input Operands allow to parameterize the ASM code; i.e., passing parameters from C code to ASM block. Again, string literals are used to specify the details.
Line 63: Line 63:


The correct thing to do when input operands are written to is to list them as dummy outputs (instead of trying to list them as clobbers).
The correct thing to do when input operands are written to is to list them as dummy outputs (instead of trying to list them as clobbers).

Here is a simple example:
<pre>
asm("mov %%eax,%%ebx": : "a" (amount));//useless but it gets the idea
</pre>

Eax will contain "amount" and be moved into ebx.


==Clobbered Registers List==
==Clobbered Registers List==

Revision as of 02:18, 27 March 2009

If you are using a GCC toolchain, using Inline Assembly might be a viable option for you. The idea is to embed assembler instructions in your C/C++ code, using the asm keyword.

Syntax

This is the prototype for calling asm() in your C/C++ code:

asm ( assembler template
    : output operands                   (optional)
    : input operands                    (optional)
    : clobbered registers list          (optional)
    );

Example:

asm ("addl %1,%0" : "=g"(x) : "g"(y));
// same as "x+=y"

Assembler Template

The Assembler Template defines the assembler instructions to inline. The default is to use AT&T syntax here. If you want to use Intel syntax, -masm=intel should be specified as a command-line option.

asm( "hlt" ); /* halts the CPU */

Output Operands

The Output Operands tell the compiler / assembler where to put the output of the assembler instructions. The Output Operands are a list of pairs, each operand consisting of a string literal stating which register to map, and a C variable to map to (in braces).

In the string literal, a refers to EAX, b to EBX, c to ECX, d to EDX, S to ESI, and D to EDI, assuming that you are coding for the IA32 architecture. An equation sign signals that this is an output operand.

int EAX;
asm( "movl $0, %%eax" : "=a" (EAX) ); /* pathetic way of EAX = 0; */

Instead of explicitly naming the register to be used, you can leave that decision to the compiler by using r in the string literal. The compiler then enumerates the operands, starting with %0:

int current_task;
asm( "ltr %0" : "=r" (current_task) );

The usage of %0, %1 etc. to enumerate the operands also explains why you had to write %%eax in the movl-example above: If you want to have one literal % in the ASM code, you have to escape it as %%.

Starting with GCC 3.1, you can use more readable labels instead of the error-prone enumeration:

int current_task;
asm( "ltr %[output]" : [output] "=r" (current_task) );

These labels are in a namespace of their own, and will not collide with any C identifiers. The same can be done for input operands, too.

Input Operands

The Input Operands allow to parameterize the ASM code; i.e., passing parameters from C code to ASM block. Again, string literals are used to specify the details.

// quite useless since you don't know the value of EDI, ECX and AL ...
asm( "cld; rep; stosb" );

// correct, but pathetic
asm( "movl %0, %%edi; movl %1, %%ecx; movb %2, %%al; cld; rep; stosb"
    : : "g"(dest),"g"(amount),"g"(value));

// more compact and still correct. GCC issues any MOVs required.
asm( "cld; rep; stosb" : : "D" (dest), "c" (amount), "a" (value) );

Note that GCC will assume that input operands are read-only (unchanged), which is obviously false in the above example.

The correct thing to do when input operands are written to is to list them as dummy outputs (instead of trying to list them as clobbers).

Here is a simple example:

asm("mov %%eax,%%ebx": : "a" (amount));//useless but it gets the idea

Eax will contain "amount" and be moved into ebx.

Clobbered Registers List

It is important to remember one thing: The C/C++ compiler knows nothing about Assembler. For the compiler, the asm statement is opaque, and if you did not specify any output, it might even come to the conclusion that it's a No-OP and optimize it away. (You can avoid this by stating asm volatile.)

Since the compiler uses CPU registers for internal optimization of your C/C++ variables, and doesn't know about ASM opcodes, you have to warn it about any registers that might get clobbered as a side effect, so the compiler can save their contents before making your ASM call.

The Clobbered Registers List is a comma-seperated list of register names, as string literals.

Wildcards: How you can let the compiler choose

You don't need to tell the compiler which register it should use in each operation, and in general, except you have good reasons to prefer one register specifically, you should better let the compiler decide for you.

Forcing to use EAX over any other register, for instance, may force the compiler to issue code that will save what was previously in eax in some other register or may introduce unwanted dependencies between operations (pipeline optimization broken)

However, the 'wildcards' can put restriction on the code the compiler will generate out of your ASM template:

"movl $0, %0" : "=g" (x) x can be whatever the compiler prefers: a register, a memory reference. It could even be a literal constant in another context.
"movl %0, %%es" : "=r" (x) you want x to go through a register. If x wasn't optimized as a register, the compiler will then move it to the place it should be. This means that "movl %0, %%es" : : "r" (0x38) is enough to load a segment register.
"outl %0, %1" : : "a" (0xFE), "N" (0x21) tells the value '0x21' can be used as a constant in the out or in operation if ranging from 0 to 255

There are of course more machine-dependent constraints you can put on the operand selection, which are listed in the info page of GCC.

See Also

There is a lot more to be known about Inline Assembly. The stack-layout of the IA32 floating-point registers can be a real headache, as well as clobbered memory etc.; further documentation and tutorials can be found here: