This page is meant to consolidate GCC's official extended asm syntax into a form that is consumable by mere mortals. It is intended to be accessible by people who know a little bit of C and a little bit of assembly.

Right below is the index of this page. Beyond that, here is some useful notation:

The assembler template contains instructions and operand placeholders. You should think of it as a format string. This string will be "printed" to the assembly file that the compiler generates. Instructions must be separated by a newline (literally

), and it's popular to prefix them with a tab or spaces (although this is not necessary). I like to end lines with "

" , which puts a cosmetic space between the instruction and the newline delimiter, and sets up indentation for the next instruction. The template is a good use case for C's string literal concatenation .

Outputs and inputs are the parameters to your assembler template "format string". They are comma-separated. They both use either one of the two following patterns:

"constraint" (expression) [Name] "constraint" (expression)

Note that although this looks metasyntactic, these are in fact the literal spellings that you must use: [Name] (when used) needs to be enclosed in square brackets, "constraint" needs to be enclosed in double quotes, and (expression) needs to be enclosed in parentheses.

"constraints" , and the kind of (expression) s that are valid, are explained in the Constraints, Outputs and Inputs sections below.

When you specify the optional [Name] field, you become able to refer to that input or output using the %[Name] syntax in the assembler template. For instance:

int foo = 1; asm("inc %[IncrementMe] " : [IncrementMe] " +r " (foo) ); // foo == 2

Even when names are specified, you can still refer to operands using the numbered syntax. Corollary: named arguments contribute to the sequence of numbered arguments. The second argument of an asm statement is always available as %1 , regardless of whether the first argument has a name or not.

Constraints

Constraint strings bridge your C expressions to assembler operands. They are necessary because the compiler doesn't know what kind of operands are valid for what instructions. In fact, for all the compiler cares, operands aren't even required to be operands to any instructions: you could use them in comments or instruction names, or not use them at all, as long as the output string is valid to the assembler. For a valid example, in "imul %0, %1, %2" : the first operand has to be a register

to be a register the second operand may be a register or a memory address

the last operand has to be a constant integer value The constraint string for each operand must communicate these requirements to GCC. For instance, it will ensure that the destination value lives in a register that can be used at the point of this statement. GCC defines many types of constraints, but on 2019's desktop/mobile platforms, those are the constraints that are the most likely to be used: r specifies that the operand must be a general-purpose register

specifies that the operand must be a general-purpose register m specifies that the operand must be a memory address

specifies that the operand must be a memory address i specifies that the operand must be an integer constant

specifies that the operand must be an integer constant g specifies that the operand must be a general-purpose register, or a memory address, or an integer constant (effectively the same as " rmi ") As implied by the g constraint, it is possible to specify multiple constraints for each argument. By specifying multiple constraints, you allow the compiler to pick the operand kind that suits it best when the same instruction has multiple forms. This is useful on x86 and not so much on ARM, because x86 overloads mnemonics with many operand types. For example, in this code: int add(int a, int b) { asm("addl %1 , %0" : " +r " (a) : " rm " (b) ); return a; } This is one way the compiler could choose to satisfy the r constraint: add: // Per x86_64 System V calling convention: // * a is held in edi. // * b is held in esi. // The return value will be held in eax. // The compiler chooses to move `a` to eax before // the add (it could arbitrarily do it after). movl %edi, %eax // `b` does not need to be moved anywhere, it is // already in a register. // The compiler can emit the addition. addl %esi, %eax // The result of the addition is returned. ret Note that when it comes to i , the satisfiability of the constraint may depend on your optimization levels. Passing an integer literal or an enum value always works, but when it comes to variables, it depends on the compiler's ability to fold constants. For instance, this will work at -O1 and above, but not -O0 , because the compiler needs to establish that x has a constant value: int x = 3; asm("int %0 " :: " i " (x) : "memory"); // [godbolt] produces "int 3" at -O1 and above; // [godbolt] errors out at -O0 GCC documents the full list of platform-independent constraints, as well as the full list of platform-specific constraints.

Outputs

Outputs specify lvalues where the result should be stored at the end of the operation. As a refresher, the concept of lvalue in C is nebulous, but something that is assignable is usually an lvalue (variables, dereferenced pointers, array subscripts, structure fields, etc). Most lvalues are accepted as operands, as long as the constraint can be respected: for instance, it's possible to pass a bitfield with r (register), but not with m (memory) as you cannot take the address of a bitfield. (The same applies to Clang's vector types). In addition, the constraint string of an output must be prefixed with either = or + . + means that the output is actually a read-write value. The operand initially has the value contained by the expression. It's fine to read from this output operand at any point in the assembly string.

means that the output is actually a read-write value. The operand initially has the value contained by the expression. It's fine to read from this output operand at any point in the assembly string. =& means that the output is an early-clobber output. Its initial value is unspecified. It is not a bug to read from an =& operand once it has been assigned a value.

means that the output is an early-clobber output. Its initial value is unspecified. It is not a bug to read from an operand once it has been assigned a value. = means that the output is write-only. The compiler can choose to give an = output the same location as an input: for that reason, it is usually a bug to write to it before the last instruction of your assembly snippet.

means that the output is write-only. The compiler can choose to give an output the same location as an input: for that reason, it is usually a bug to write to it before the last instruction of your assembly snippet. =@ccCOND is a special case of = that allows you to query the result of a condition code at the end of your assembly statement. You cannot reference a condition output in your assembly template. For instance, almost all x86 instructions read and write to their first operand: you need to use the + prefix to communicate that. A simple example would be: asm("addl %1 , %0" : " +rm " (foo) : " g " (bar) ) The initial value of foo is important here, because it's what bar will be added to. (The whole constraint string additionally specifies that foo may be referenced in the assembler string as a register or a memory address, since x86 has instruction forms for both.) On the other hand, almost no ARM instruction uses the initial value of its destination register. In those cases, you would use = to communicate that. The ARM equivalent to the above would be: asm("add %0 , %1 , %2 " : " =r " (foo) : " r " (foo) , " r " (bar) ) (ARM only supports computations on registers, so all the operands are register operands.) When you use = for an output, the compiler may reuse a location that was used as an input. This can lead to incorrect code generation if you also read from that output. For instance, in this assembly code: asm("movl $123, %0

" "addl %1 , %0 " : " =&r "(foo) : " r "(bar) ); The =& constraint is necessary: the mov that writes to %0 isn't the last instruction of the sequence, and this asm statement has inputs. If we used = , the compiler could have selected the same register for %1 and %0 ! Note that this works and it is safe if your statement has a single instruction, such as in the ARM case above. However, with our two-instruction example, instead of doing the equivalent of foo = bar + 123 , this code could now be doing "movl $123, %eax; addl %eax, eax" , which is just foo = 246 . You solve this problem by using =& , which tells the compiler that it can't use the same location as it used for an input. Condition code outputs =@ccCOND is a special case of = that allows you to get the value of a condition code at the end of your assembly code. You must replace COND with the architecture-dependent name of the condition code that you want to query. For x86, the entire list of possible conditions can be found in the setcc documentation. For instance, =@ccnz will fill your output with the result of the setnz instruction (true if the result is non-zero, false otherwise). You cannot use a condition code operand in your assembly string, even as it contributes to the numbering of operands. As a concrete example (of different flags): // [godbolt] asm("subq %3 , %2 " : " =@ccc "(*carry) , " =@cco "(*overflow) : "g"(left) , "g"(right) ); The carry flag takes slot 0, and the overflow takes slot 1, even though they cannot be referenced.

Inputs