You copied the Doc URL to your clipboard.

Global named register variables

The compiler enables you to use the register storage class specifier to store global variables in core registers. These variables are called global named register variables.

Syntax

register Type VariableName __asm("Reg")

Parameters

Type
The data type of variable. The data type can be char or any 8-bit, 16-bit, or 32-bit integer type, or their respective pointer types.
VariableName
The name of the variable.
Reg
The core register to use to store the variable. The core register can be R5 to R11.

Restrictions

This feature is only available for AArch32 state.

If you use -mpixolib, then you must not use the following registers as global named register variables:

  • R8
  • R9

If you use -fwrpi or -fwrpi-lowering, then you must not use register R9 as a global named register variable.

Arm recommends that you do not use the following registers as global named register variables because the Arm ABI reserves them for use as a frame pointer if needed. You must carefully analyze your code, to avoid side effects, if you want to use these registers as global named register variables:

  • R7 in T32 state.
  • R11 in A32 state.
Code size

Declaring a core register as a global named register variable means that the register is not available to the compiler for other operations. If you declare too many global named register variables, code size increases significantly. In some cases, your program might not compile, for example if there are insufficient registers available to compute a particular expression.

Operation

Using global named register variables enables faster access to these variables than if they were stored in memory.

Note

For correct runtime behavior:

  • You must use the relevant -ffixed-rN option for all the registers that you use as a global named register variable.
  • You must use the relevant -ffixed-rN option to compile any source file that contains calls to external functions that use global named register variables.

For example, to use register R5 as a global named register for an integer foo, you must use:

register int  foo  __asm("R5")

For the above example, you must ensure to compile with the command-line option -ffixed-r5. For more information, see -ffixed-rN.

The Arm standard library has not been built with any -ffixed-rN option. If you want to link application code containing global named register variables with the Arm standard library, then:

  • To ensure correct runtime behavior, ensure that the library code does not call code that uses the global named register variables in your application code.
  • The library code might push and pop the register to stack, even if your application code uses this register as a global named register variable.

Note

  • If you use the register storage class, then you cannot use any additional storage class such as extern, static, or typedef for the same variable.
  • In C, global named register variables cannot be initialized at declaration. In C++, any initialization is treated as dynamic initialization.

Examples

The following example demonstrates the use of register variables and the relevant -ffixed-rN option.

Source file main.c contains the code below:

#include <stdio.h>

/* Function defined in another file that will be compiled with
   -ffixed-r5 -ffixed-r6. */
extern int add_ratio(int a, int b, int c, int d, int e, int f);

/* Helper variable */
int other_location = 0;

/* Named register variables */
register int foo __asm("r5");
register int *bar __asm("r6");

__attribute__((noinline))  int initialise_named_registers(void)
{
    /* Initialise pointer-based named register variable */
    bar = &other_location;

    /* Test using named register variables */
    foo = 1000;
    *bar = *bar + 1;
    return 0;
}

int main(void)
{
   initialise_named_registers();
   add_ratio(10, 2, 30, 4, 50, 6);
   printf("foo: %d\n", foo);  // should print 1000
   printf("bar: %d\n", *bar); // should print 1
}

Source file sum.c contains the code below:

/* Arbitrary function that could normally result in the compiler using R5 and R6.
When compiling with -ffixed-r5 -ffixed-r6, the compiler should not use registers 
R5 or R6 for any function in this file.
*/
__attribute__((noinline)) int add_ratio(int a, int b, int c, int d, int e, int f)
{
   int sum;
   sum = a/b + c/d + e/f;
   if (a > b && c > d)
     return sum*e*f;
   else
     return (sum/e)/f;
}

Compile main.c and sum.c separately before linking them. This application uses global named register variables using R5 and R6, and therefore both source files must be compiled with the relevant -ffixed-rN option:

armclang --target=arm-arm-none-eabi -march=armv8-a -O2 -ffixed-r5 -ffixed-r6 -c main.c -o main.o --save-temps
armclang --target=arm-arm-none-eabi -march=armv8-a -O2 -ffixed-r5 -ffixed-r6 -c sum.c -o sum.o --save-temps

Link the two object files using armlink:

armlink --cpu=8-a.32 main.o sum.o -o image.axf

The use of the armclang option --save-temps enables you to look at the generated assembly code. The file sum.s has been generated from sum.c, and does not use registers R5 and R6 in the add_ratio() function:

add_ratio:
        .fnstart
@ %bb.0:
        .save   {r4, r7, r11, lr}
        push    {r4, r7, r11, lr}
        ldr     r12, [sp, #20]
        sdiv    r4, r2, r3
        ldr     lr, [sp, #16]
        sdiv    r7, r0, r1
        add     r4, r4, r7
        cmp     r0, r1
        sdiv    r7, lr, r12
        cmpgt   r2, r3
        add     r4, r4, r7
        bgt     .LBB0_2
@ %bb.1:
        sdiv    r0, r4, lr
        sdiv    r0, r0, r12
        pop     {r4, r7, r11, pc}
.LBB0_2:
        mul     r0, r12, lr
        mul     r0, r0, r4
        pop     {r4, r7, r11, pc}

The file main.s has been generated from main.c, and uses registers R5 and R6 only for the code that directly uses these global named register variables:

initialise_named_registers:
        .fnstart
@ %bb.0:
        movw    r6, :lower16:other_location
        mov     r5, #1000
        movt    r6, :upper16:other_location
        ldr     r0, [r6]
        add     r0, r0, #1
        str     r0, [r6]
        mov     r0, #0
        bx      lr
main:
        .fnstart
@ %bb.0:
        .save   {r11, lr}
        push    {r11, lr}
        .pad    #8
        sub     sp, sp, #8
        bl      initialise_named_registers
        mov     r0, #6
        mov     r1, #50
        str     r1, [sp]
        mov     r1, #2
        str     r0, [sp, #4]
        mov     r0, #10
        mov     r2, #30
        mov     r3, #4
        bl      add_ratio
        adr     r0, .LCPI1_0
        mov     r1, r5
        bl      __2printf
        ldr     r1, [r6]
        adr     r0, .LCPI1_1
        bl      __2printf
        mov     r0, #0
        add     sp, sp, #8
        pop     {r11, pc}
        .p2align        2

Note

The Arm standard library code, such as the library implementations for the printf() function, might still use R5 and R6 because the standard library has not been built with any -ffixed-rN option.
Was this page helpful? Yes No