You can define a local register variable and associate it with a specified register like this:
register int *foo asm ("r12");
Here r12
is the name of the register that should be used. Note
that this is the same syntax used for defining global register variables,
but for a local variable the declaration appears within a function. The
register
keyword is required, and cannot be combined with
static
. The register name must be a valid register name for the
target platform.
Do not use type qualifiers such as const
and volatile
, as
the outcome may be contrary to expectations. In particular, when the
const
qualifier is used, the compiler may substitute the
variable with its initializer in asm
statements, which may cause
the corresponding operand to appear in a different register.
As with global register variables, it is recommended that you choose a register that is normally saved and restored by function calls on your machine, so that calls to library routines will not clobber it.
The only supported use for this feature is to specify registers
for input and output operands when calling Extended asm
(see Extended Asm - Assembler Instructions with C Expression Operands). This may be necessary if the constraints for a
particular machine don’t provide sufficient control to select the desired
register. To force an operand into a register, create a local variable
and specify the register name after the variable’s declaration. Then use
the local variable for the asm
operand and specify any constraint
letter that matches the register:
register int *p1 asm ("r0") = …; register int *p2 asm ("r1") = …; register int *result asm ("r0"); asm ("sysint" : "=r" (result) : "0" (p1), "r" (p2));
Warning: In the above example, be aware that a register (for example
r0
) can be call-clobbered by subsequent code, including function
calls and library calls for arithmetic operators on other variables (for
example the initialization of p2
). In this case, use temporary
variables for expressions between the register assignments:
int t1 = …; register int *p1 asm ("r0") = …; register int *p2 asm ("r1") = t1; register int *result asm ("r0"); asm ("sysint" : "=r" (result) : "0" (p1), "r" (p2));
Defining a register variable does not reserve the register. Other than
when invoking the Extended asm
, the contents of the specified
register are not guaranteed. For this reason, the following uses
are explicitly not supported. If they appear to work, it is only
happenstance, and may stop working as intended due to (seemingly)
unrelated changes in surrounding code, or even minor changes in the
optimization of a future version of gcc:
asm
asm
without using input
or output operands.
Some developers use Local Register Variables in an attempt to improve gcc’s allocation of registers, especially in large functions. In this case the register name is essentially a hint to the register allocator. While in some instances this can generate better code, improvements are subject to the whims of the allocator/optimizers. Since there are no guarantees that your improvements won’t be lost, this usage of Local Register Variables is discouraged.
On the MIPS platform, there is related use for local register variables with slightly different characteristics (see Defining coprocessor specifics for MIPS targets in GNU Compiler Collection (GCC) Internals).