Keil Logo

Writing inline assembly code

6.3 Writing inline assembly code

The compiler provides an inline assembler that enables you to write assembly code in your C or C++ source code, for example to access features of the target processor that are not available from C or C++.

The __asm keyword can incorporate inline assembly code into a function using the GNU inline assembly syntax. For example:

#include <stdio.h>

int add(int i, int j)
  int res = 0;
  __asm ("ADD %[result], %[input_i], %[input_j]"
    : [result] "=r" (res)
    : [input_i] "r" (i), [input_j] "r" (j)
  return res;

int main(void)
  int a = 1;
  int b = 2;
  int c = 0;

  c = add(a,b);

  printf("Result of %d + %d = %d\n", a, b, c);


The inline assembler does not support legacy assembly code written in armasm assembler syntax. See the Migration and Compatibility Guide for more information about migrating armasm syntax assembly code to GNU syntax.

The general form of an __asm inline assembly statement is:

__asm [volatile] (code); /* Basic inline assembly syntax */
/* Extended inline assembly syntax */ 
__asm [volatile] (code_template 
       : output_operand_list 
      [: input_operand_list 
      [: clobbered_register_list]] 

Use the volatile qualifier for assembler instructions that have processor side-effects, which the compiler might be unaware of. The volatile qualifier disables certain compiler optimizations, which may otherwise lead to the compiler removing the code block. The volatile qualifier is optional, but you should consider using it around your assembly code blocks to ensure the compiler does not remove them when compiling with -O1 or above.

code is the assembly instruction, for example "ADD R0, R1, R2". code_template is a template for an assembly instruction, for example "ADD %[result], %[input_i], %[input_j]".

If you specify a code_template rather than code then you must specify the output_operand_list before specifying the optional input_operand_list and clobbered_register_list.

output_operand_list is a list of output operands, separated by commas. Each operand consists of a symbolic name in square brackets, a constraint string, and a C expression in parentheses. In this example, there is a single output operand: [result] "=r" (res). The list can be empty. For example:

__asm ("ADD R0, %[input_i], %[input_j]"
    :  /* This is an empty output operand list */
    : [input_i] "r" (i), [input_j] "r" (j)

input_operand_list is an optional list of input operands, separated by commas. Input operands use the same syntax as output operands. In this example, there are two input operands: [input_i] "r" (i), [input_j] "r" (j). The list can be empty.

clobbered_register_list is a comma-separated list of strings. Each string is the name of a register that the assembly code potentially modifies, but for which the final value is not important. To prevent the compiler from using a register for a template string in an inline assembly string, add the register to the clobber list.

For example, if a register holds a temporary value, include it in the clobber list. The compiler avoids using a register in this list as an input or output operand, or using it to store another value when the assembly code is executed.

The list can be empty. In addition to registers, the list can also contain special arguments:

The instruction affects the condition code flags.
The instruction accesses unknown memory addresses.

The registers in clobbered_register_list must use lowercase letters rather than uppercase letters. An example instruction with a clobbered_register_list is:

__asm ("ADD R0, %[input_i], %[input_j]"
    :  /* This is an empty output operand list */
    : [input_i] "r" (i), [input_j] "r" (j)
    : "r5","r6","cc","memory" /*Use "r5" instead of "R5" */

Defining symbols and labels

You can use inline assembly to define symbols. For example:

__asm (".global __use_no_semihosting\n\t");

To define labels, use : after the label name. For example:

__asm ("my_label:\n\t");

Multiple instructions

You can write multiple instructions within the same __asm statement. This example shows an interrupt handler written in one __asm statement for an Arm®v8‑M mainline architecture.

void HardFault_Handler(void)
  asm (
    "TST LR, #0x40\n\t"
    "BEQ from_nonsecure\n\t"
    "TST LR, #0x04\n\t"
    "ITE EQ\n\t"
    "MRSEQ R0, MSP\n\t"
    "MRSNE R0, PSP\n\t"
    "B hard_fault_handler_c\n\t"
    "MRS R0, CONTROL_NS\n\t"
    "TST R0, #2\n\t"
    "ITE EQ\n\t"
    "MRSEQ R0, MSP_NS\n\t"
    "MRSNE R0, PSP_NS\n\t"
    "B hard_fault_handler_c\n\t"

Copy the above handler code to file.c and then you can compile it using:

armclang --target=arm-arm-none-eabi -march=armv8-m.main -S file.c -o file.s

Embedded assembly

You can write embedded assembly using __attribute__((naked)). For more information, see __attribute__((naked)) in the Arm Compiler Reference Guide.

Non-ConfidentialPDF file icon PDF version100748_0616_01_en
Copyright © 2016–2021 Arm Limited or its affiliates. All rights reserved. 
  Arm logo
Important information

This site uses cookies to store information on your computer. By continuing to use our site, you consent to our cookies.

Change Settings

Privacy Policy Update

Arm’s Privacy Policy has been updated. By continuing to use our site, you consent to Arm’s Privacy Policy. Please review our Privacy Policy to learn more about our collection, use and transfers
of your data.