Timeline

2025-09-27

init


Reference documentation:

Inline Assembly Language — embedding assembly code within C code.

Purpose:

  • Optimization: optimize specific time-sensitive code.
  • C code needs access to certain special instructions for special functionality, such as memory barrier instructions.

Two modes of inline assembly:

  • Basic inline assembly
  • Extended inline assembly

Basic Inline Assembly

Format:

1
asm asm-qualifiers(AssemblerInstructions)
  • asm keyword: indicates this is a GNU extension.
  • Qualifiers:
    • volatile: typically not needed in basic inline assembly.
    • inline: the asm assembly code will be as minimal as possible.
  • Assembly code block (AssemblerInstructions):
    • GCC treats the inline assembly as a string.
    • GCC does not parse or analyze the inline assembly.
    • Multiple assembly instructions need to be separated by \n\t.
    • GCC’s optimizer may reorder assembly instructions. If you need to preserve order, use multiple inline assembly blocks.

Basic inline assembly example

Extended Inline Assembly

Extended inline assembly

  • Format:
    • asm keyword: indicates this is a GNU extension.
    • Qualifiers (asm-qualifiers):
      • volatile: disables GCC optimization.
      • inline: the asm assembly code will be as minimal as possible.
      • goto: after the inline assembly, jump to a C label.

Extended inline assembly example

  • Output operands:

    Describes C variables that can be modified within the instruction block, along with constraints.

    • Each output constraint typically starts with =, followed by a letter indicating the operand type, then the constraint for variable binding.
    • Output operands usually use = or + as output constraints. = means the modified operand is write-only; + means it is read-write.
    • Output operands can be empty.
1
"=/+" + constraint modifier + variable

Common constraint modifiers:

  • r: A register operand is allowed provided it is in a general register.
  • Input operands:

    Describes C variables that can only be read within the instruction block, along with constraints.

    • Input operand parameters are read-only. Do not attempt to modify input operand contents, because GCC assumes input operand contents are consistent before and after the inline assembly.
    • You cannot use = or + constraints in input operands; the compiler will report an error.
    • Input operands can be empty.
  • Clobbers:

    • Assembly code may modify registers besides the output registers.
    • If not communicated to the compiler, the compiler assumes these registers remain unchanged, potentially leading to errors or unpredictable behavior.
    • Clobber also acts as a memory barrier (especially "memory"), ensuring the compiler does not reorder or cache variables.
Name Purpose
"x0", "x1", … General-purpose registers modified
"cc" Condition code register (flags) modified
"memory" Assembly accessed memory; ensures compiler flushes registers to memory and reloads
"redzone" Using stack space in x86-64 redzone area
  • "memory" tells GCC that the inline assembly modified memory values, forcing the compiler to flush all cached values before executing the assembly and reload them afterward — this prevents compiler reordering.
  • "cc" indicates the inline assembly modified status register flags.
  • Do not list the stack pointer register (esp/rsp) in the clobber list.
  • Do not duplicate output registers.
  • The four types can be combined with commas.
  • Instruction operand references:
    • %0 corresponds to the first parameter in output/input operands, %1 to the second, etc.

Instruction operands

Example from the Linux kernel

Output and Input Constraint Modifiers

GCC inline operators and modifiers

Output and Input Constraint Modifiers — General

General constraint modifiers

Output and Input Constraint Modifiers — ARM64

ARM64-specific constraint modifiers

Assembly Symbolic Names Instead of % Prefix

  • %[name] → reference a constraint variable.
  • %w[name] → reference the lower 32-bit register (e.g., w0, w1).
  • %x[name] → reference the full 64-bit register (e.g., x0, x1).

Assembly symbolic names instead of % prefix

Experiment 1: Implementing a Simple memcpy Function

Experiment 1

Experiment code

Pitfalls and Traps

  • GDB cannot single-step through inline assembly.
  • Output and input constraint modifiers must not be misused, otherwise the program will malfunction.

Experiment 3: Implementing memset Using Inline Assembly

Experiment 3 code

Advanced Inline Assembly: Combining with Macros

  • Technique 1: Using C’s # operator. In parameterized macros, # acts as a preprocessing operator that converts a token to a string.

Linux kernel example

1
2
3
4
5
6
7
8
9
#define ATOMIC_OP(op, asm_op) \
static inline void atomic_##op(int i, atomic_t *v) { \
__asm__ __volatile__( \
asm_op " %1, %0" \
: "+m" (v->counter) \
: "ir" (i)); \
}


This macro can be used to generate multiple atomic operation functions:

1
2
ATOMIC_OP(add, "addl")
ATOMIC_OP(sub, "subl")

Which expands to:

1
2
3
4
5
6
7
8
9
10
11
12
13
static inline void atomic_add(int i, atomic_t *v) {
__asm__ __volatile__(
"addl %1, %0"
: "+m" (v->counter)
: "ir" (i));
}

static inline void atomic_sub(int i, atomic_t *v) {
__asm__ __volatile__(
"subl %1, %0"
: "+m" (v->counter)
: "ir" (i));
}

The macro in the image uses #asm_op because it needs to convert the macro parameter asm_op into a string literal for string concatenation or output. This syntax is called macro stringification.

asm_op is a macro parameter that is replaced with your provided value during macro expansion (e.g., "addl").

It is already a string, so when you call:

1
ATOMIC_OP(add, "addl")

After macro expansion:

1
2
3
4
5
__asm__ __volatile__(
"addl %1, %0"
: "+m" (v->counter)
: "ir" (i));

Whereas:

1
ATOMIC_OP(add, addl)

After macro expansion becomes:

1
2
3
4
__asm__ __volatile__(
addl "%1, %0"
: "+m" (v->counter)
: "ir" (i));

Which is not what we want.

##nameToken Pasting Operator

  • Concatenates macro parameters with preceding/following identifiers into a new identifier (not a string).
  • Commonly used to generate variable names, function names, etc.
1
2
3
#define MAKE_FUNC(name) void func_##name(void) {}

MAKE_FUNC(test); // Expands to: void func_test(void) {}

Experiment 4: Combining Inline Assembly with Macros

Experiment 4

Experiment 4 code

Experiment 5: Implementing Macros for Reading/Writing System Registers

Experiment 5

Experiment 5 code

This uses GNU C statement expression syntax:

What is ({ ... })

  • This is a GNU extension, not standard C.

  • It allows a code block to execute like a statement while also returning a value.

  • Syntax rule:

    ({ statement1; statement2; ...; expression; })
    The last expression (without a semicolon) in the block is the return value.

Experiment 5 alternative approach

Inline Assembly: goto

The goto template of inline assembly can jump to C language labels.

goto inline assembly

  • The goto template’s output operands must be empty.
  • A new gotolabels section lists C labels that are allowed as jump targets.

goto inline assembly example

Experiment 6: goto Template Inline Assembly

Experiment 6

Experiment 6 code

%l[label] is a special syntax in GCC inline assembly for asm goto, indicating a jump to the C code label label.

Detailed explanation:

  • %l[...] tells the compiler this is a label symbol, not a regular register or immediate.
  • label is the label name you defined in your C code, such as label: in your code.
  • asm goto allows assembly code to directly jump to a C code label via conditional branches, implementing conditional branching.