Cogs and Levers A blog full of technical stuff

An inline assembly primer with gcc

A really handy feature of GCC is that it allows you to inter-mix assembly code with your C code.

There are so many great references on this topic already. One of the best is from Lockless however there are many, many more.

Today’s article is more of a quick reference for inlining your assembly code.

Syntax

When preparing a block of assembly to be put inline in your C code, you use the asm keyword.

asm [volatile] ( 
	assembly code
	: outputs
	: inputs
	: clobbers
)

The first parameter that is passed is the assembly code itself. It’ll be in AT&T syntax, but will also have some extra rules apply to it which will allow for the compiler to make some decisions for you. The outputs, inputs and clobbers are optional lists consisting of directives instructing the compiler how to handle inputs, outputs and what’s expected to be trashed (clobbered) in your assembly block.

A simple example usage, to add two integers and return the result might look like this:

int add(int a, int b) {
	int c;

	asm (
		"xorl	%%eax, %%eax\n\t"
		"addl	%2, %1\n\t"
		"movl 	%1, %0"
		: "=m" (c)
		: "r" (a), "r" (b)
		: "%eax"
	);

	return c;
}

Once this code is compiled, you can see what the compiler has done with it:

	asm (
   a:	8b 55 ec             	mov    edx,DWORD PTR [rbp-0x14]
   d:	8b 4d e8             	mov    ecx,DWORD PTR [rbp-0x18]
  10:	31 c0                	xor    eax,eax
  12:	01 ca                	add    edx,ecx
  14:	89 55 fc             	mov    DWORD PTR [rbp-0x4],edx
		: "=m" (c)
		: "r" (a), "r" (b)
		: "%eax"
	);

edx and ecx were chosen as our general purpose registers for inputs, so they’re loaded first-up. The addition occurs and then the result (as requested) is placed in the memory location of our output. Back in the inline code, you can see that these registers have been symbolically referenced as %1, %2, etc.

Outputs are a mix of constraints and modifiers, inputs are just constraints and clobbers list out what was modified (register-wise or other).

What about volatile?

The volatile keyword allows you to tell the compiler to not optimise away our code if it deems that it isn’t required (i.e. is has no effect on anything).

Constraints

Constraint Description
m Any kind of a memory address
o Memory address if it’s offsettable
V Memory address if it’s not offsettable
< Memory with autodecrement addressing
> Memory with autoincrement addressing
r General purpose register
i Immediate integer value
n Immediate integer with a known value
I . . P Range based immediate integer values
E Immediate format-dependent floating point number
F Immediate floating point number
G, H Range based immediate float values
s Immediate integer that is not an explicit integer
g Any register, memory or immediate value; not a general purpose register though
X Any operand is allowed
p Any operand that is a valid memory address

A full description of all of these constraints can be found here.

Modifiers

Modifier Description
= Operand is written to
+ Operand is read from and written to
& Operand is written to (clobbered) before input operands are used
% Instruction is cumulative for this operand

A full description of all of these modifiers can be found here.

Clobbers

Clobber Description
cc Flags are modified
memory Memory outside of what is in the constraints is modified

A full description of clobbers can be found here.