7.14 Inline assembler and register access in C and C++ code

The inline assembler provides no direct access to the physical registers of an ARM processor. If an ARM register name is used as an operand in an inline assembler instruction it becomes a reference to a variable of the same name, and not the physical ARM register.

The variable can be thought of as a virtual register.

The compiler declares variables for physical registers as appropriate during optimization and code generation. However, the physical register used in the assembled code might be different to that specified in the instruction, or it might be stored on the stack. You can explicitly declare variables representing physical registers as normal C or C++ variables. The compiler implicitly declares registers R0 to R12 and r0 to r12 as auto signed int local variables, regardless of whether or not they are used. If you want to declare them to be of a different data type, you can do so. For example, in the following code, the compiler does not implicitly declare r1 and r2 as auto signed int because they are explicitly declared as char and float types respectively:

void bar(float *);
int add(int x)
{
  int a = 0;
  char r1 = 0;
  float r2 = 0.0;
  bar(&r2);
  __asm
  {
    ADD r1, a, #100
  }
  ...
  return r1;
}

The compiler does not implicitly declare variables for any other registers, so you must explicitly declare variables for registers other than R0 to R12 and r0 to r12 in your C or C++ code. No variables are declared for the sp (r13), lr (r14), and pc (r15) registers, and they cannot be read or directly modified in inline assembly code.

There is no virtual Processor Status Register (PSR). Any references to the PSR are always to the physical PSR.

The size of the variables is the same as the physical registers.

The compiler-declared variables have function local scope, that is, within a single function, multiple asm statements or declarations that reference the same variable name access the same virtual register.

Existing inline assembly code that conforms to previously documented guidelines continues to perform the same function as in previous versions of the compiler, although the actual registers used in each instruction might be different.

The initial value in each variable representing a physical register is unknown. You must write to these variables before reading them. The compiler generates an error if you attempt to read such a variable before writing to it, for example, if you attempt to read the variable associated with the physical register r1.

Any variables that you use in inline assembly code to refer to registers must be explicitly declared in your C or C++ code, unless they are implicitly declared by the compiler. However, it is better to explicitly declare them in your C or C++ code. You do not have to declare them to be of the same data type as the implicit declarations. For example, although the compiler implicitly declares register R0 to be of type signed int, you can explicitly declare R0 as an unsigned integer variable if required.

It is also better to use C or C++ variables as instruction operands. The compiler generates a warning the first time a variable or physical register name is used, regardless of whether it is implicitly or explicitly declared, and only once for each translation unit. For example, if you use register r3 without declaring it, a warning is displayed. You can suppress the warning with --diag_suppress.

Non-ConfidentialPDF file icon PDF versionARM DUI0472M
Copyright © 2010-2016 ARM Limited or its affiliates. All rights reserved.