SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Syntax for inline assembly operands in GCC
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm

Syntax for inline assembly operands in GCC

Jeday Schwartz
Last updated: October 5, 2023 9:40 am
Jeday Schwartz 9 Min Read
Share
SHARE

The GNU Compiler Collection (GCC) provides an inline assembler that allows embedding assembly language code into C and C++ programs. This can be useful for writing time-critical code segments, accessing processor-specific instructions, or implementing code requiring direct hardware access.

Contents
Operand SyntaxSpecifying Operand ConstraintsMatching Constraints to OperandsModifiersSpecifying ClobbersNaming OperandsPutting It Together: ExamplesSpecial Operand NamesMemory OperandsOffsettable Memory ReferencesOperand Size and TypeType SpecifiersExtended Asm StatementsThings to Keep in MindConclusion

One of the key components of using GCC’s inline assembler is properly defining the operands that are passed between the assembly code and surrounding C/C++ code. This article provides an overview of the syntax and features for specifying inline assembly operands in GCC.

Operand Syntax

Operands are specified using the following syntax: [constraint](clobbers)[, return] [asm symbolic name]

The components in brackets are optional:

  • constraint – specifies requirements for matching input/output operands
  • clobbers – list of clobbered registers
  • return – specifies operand that represents assembly instruction output
  • asm symbolic name – symbolic name representing the operand in assembly code

For example: “r” (input_var)

This defines an input operand by specifying a “r” register constraint and giving it the symbolic name “input_var”. The surrounding quotes are required.

Specifying Operand Constraints

The constraint specifies the requirements for the matching operand. This tells GCC how to reconcile the types between the inline assembly and surrounding C/C++ code.

Some common constraint codes are:

  • r – general purpose register
  • g – any register, memory or immediate integer
  • m – memory operand
  • i – immediate integer
  • o – offsettable memory
  • X – any operand

Multiple constraint alternatives can be specified, separated by commas. For example, “rm” would match a register or memory operand.

Matching Constraints to Operands

GCC will try to match input operands to the assembler instruction template based on order and compatible types. Output operands represent values that are written by the assembly code.

For example: asm (“add %1, %2, %0” : “=r” (result) : “r” (input1), “r” (input2));

This will add the registers input1 and input2, storing the result in result. GCC handles moving the values in and out of the registers as needed.

Modifiers

Additional modifiers can be used on constraints to alter matching behavior:

  • & – early clobber operand
  • + – operand is writable (output)
  • % – operand is both readable and writable
  • ? – ignore operand if it doesn’t match

Specifying Clobbers

The optional clobbers list indicates any registers or resources that are overwritten (clobbered) by the inline assembly code, separate by commas. For example: asm (“instr” : : : “r1”, “r2”, “cc”);

This indicates that assembly instruction “instr” modifies registers r1, r2, and the condition codes register cc.

Naming Operands

The symbolic operand name provides a way to reference the operand from within the assembly code. For example: asm (“mov %[result], #1” : [result] “=r” (myresult));

This allows the assembly code to use the symbolic name “result” rather than having to hardcode register numbers.

Putting It Together: Examples

To tie the key concepts together, here are some examples of inline assembly operands in GCC: int input = 10; int result; // Simple input operand asm (“dbl %[input]” : : [input] “r” (input)); // Input and output operand asm (“add %[input], #1, %[result]” : [result] “=r” (result) : [input] “r” (input)); // Multiple inputs, one output asm (“mul %[in1], %[in2], %[out]” : [out] “=r” (result) : [in1] “r” (input1), [in2] “r” (input2)); // Early clobber operand asm (“load %[addr], %[out]” : [out] “+r” (result) : [addr] “r” (address) : “memory”);

In the first example, the “r” constraint matches the input variable to a register operand expected by the assembly instruction. In the second, an output operand is added to represent the result. The third shows multiple inputs, and the fourth demonstrates an early clobber operand using “+r” to indicate it is writable.

Special Operand Names

There are some special symbolic names that can be used:

  • %0 – First output operand
  • %1, %2, etc. – Subsequent output operands
  • % – Corresponding input operand by position

For example: asm (“op %1, %2, %0” : “=r” (result) : “r” (input1), “r” (input2));

The %1 and %2 refer to input1 and input2 respectively based on the order, %0 refers to the output result.

Memory Operands

Memory operands represent dereferenced addresses to read/write memory in the inline assembly.

For example: int value = 0; asm (“ldr %[result], [%[addr]]” : [result] “=r” (result) : [addr] “r” (&value));

This loads the value at address &value into the result. The “r” constraint matches the C pointer to a register expected by the ldr instruction.

Offsettable Memory References

GCC allows dereferencing register plus offset addresses like: asm (“ldrb r1, [r2, #8]” : : : );

This can be represented in constraints by: “o” (*(struct_pointer + 8))

Which would match a register and offset memory operand.

Operand Size and Type

The size and type of operands is important for matching. For example, a 32-bit integer vs 64-bit long long int.

Size specifiers can be added to constraints to indicate required operand sizes:

  • b – byte (8 bit)
  • h – halfword (16 bit)
  • w – word (32 bit)
  • g – giant word (64 bit)

For example: long long result; asm (“op %1, %2, %0” : “=r” (result) : “ri” (32), “ri” (32));

This forces input operands to 32-bit integers even if long long would normally match a register.

Type Specifiers

GCC also supports explicit type specifiers that can override the default matching behavior:

  • i – signed integer
  • u – unsigned integer
  • n – immediate integer
  • E – float
  • F – double
  • X – long double

Adding these after normal register/memory constraints forces GCC to convert the C values to the specified type before passing into the assembler code.

Extended Asm Statements

GCC supports extended asm syntax that allows assembly instructions to be embedded directly in C/C++ code without requiring semicolons and output operands: asm volatile (“add %[input], #1” : [input] “+r” (input));

The volatile ensures instructions execute in order. Extended asm is compatible with normal constraints and clobbers.

Things to Keep in Mind

Here are some general guidelines when using inline assembly with GCC:

  • Ensure constraint codes match expected operand types in assembly instructions.
  • Specify all clobbered registers and memory to prevent conflicts.
  • Use size/type specifiers if relying on specific operand sizes.
  • Give unique symbolic names to all operands.
  • Check compiler documentation for available constraints and modifiers.
  • Validate assembly output using compiler optimizations like -S.

Properly specifying constraints and operands is key to avoiding issues when mixing inline assembly with GCC generated code.

Conclusion

GCC’s inline assembler provides a flexible way to embed assembly code in C/C++ programs. The key syntax includes constraints for matching input/output operands, clobbers for managing side effects, and symbolic names for referencing operands from assembly code. Constraint codes like “r”, “m”, and “i” specify register, memory, and immediate integer operands. Modifiers like “&” and “+” alter matching behavior. Type and size specifiers force specific operand types. Following best practices for inline assembly coding can help create stable mixed C/C++ and assembly code.

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article Inline assembly in C code for Cortex-M0/M0+
Next Article Cortex-M0 hangs after performing soft reset
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

GNU ARM Embedded Toolchain

The GNU ARM Embedded Toolchain is a complete open-source toolchain…

9 Min Read

Arm Cortex M3 Architecture

The Arm Cortex-M3 is a 32-bit processor core licensed by…

8 Min Read

What are the pipeline stages of the Cortex-M3?

The Cortex-M3 is a 32-bit RISC processor designed by ARM…

7 Min Read

Using Keil RTX RTOS on Cortex-M0+

The Cortex-M0+ processor is an ultra low power 32-bit ARM…

7 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account