6.12.6.3 Hard Register Constraints

Similar to register asm but still distinct, hard register constraints are another way to force operands of inline asm into specific machine registers. In contrast to register asm where a variable is bound to a machine register, a hard register constraint binds an asm operand to a machine register. Assume in the following that r4 is a general-purpose register, f5 a floating-point register, and v6 a vector register for some target.

int x;
int y __attribute__ ((vector_size (16)));
...
asm ("some instructions"
     : "={r4}" (x)
     : "{f5}" (42.0), "{v6}" (y));

For the inline asm, variable x is bound to register r4, and y is loaded to v6. Furthermore, constant 42.0 is loaded into floating-point register f5.

A key difference between register asm and hard register constraints is that the latter are specified at the point where they are supposed to materialize, namely at inline asm, which may lead to more readable code.

Usage

Each input operand is loaded into the register specified by its corresponding hard register constraint. Furthermore, each hard register must be used at most once among an alternative for inputs. This renders hard register constraints more strict compared to register asm where multiple inputs may share a register as for example in

int x;
register int y asm ("0") = ...;
asm ("" : "=r" (x) : "r" (y), "r" (y));

or even

register int x asm ("0") = 42;
register int y asm ("0") = 24;
asm ("" : "=r" (x) : "r" (x), "r" (y));

The analogue for hard register constraints is invalid in order to prevent subtle bugs.

Likewise, two outputs must not share a register among an alternative. That means, the following example is invalid

int x, y;
asm ("" : "={r4}" (x), "={r4}" (y));  // invalid

which also aligns with register asm. Despite that, each output must refer to a distinct object if a hard register constraint is involved. For example, in the following, object x is assigned two registers.

int x;
asm ("" : "=r" (x), "=r" (x));

This is not allowed for hard register constraints in order to prevent subtle bugs. Even if only one output operand has a hard register constraint, the code is rejected since the allocation for the object is still ambiguous.

int x;
asm ("" : "=r" (x), "={1}" (x));  // invalid

The type of an operand must be supported by the corresponding machine register.

A hard register constraint may refer to any general, floating-point, or vector register except a fixed register as e.g. the stack-pointer register. The set of allowed registers is target dependent analogue to register asm. Furthermore, the referenced register must be a valid register name of the target. Note, on some targets, a single register may be referred to by different names where each name specifies the length of the register. For example, on x86_64 the register names rcx, ecx, and cx all refer to the same register but in different sizes. If any of those names is used for a hard register constraint, the actual size of a register is determined by its corresponding operand. For example

long x;
asm ("mov\t$42, %0" : "={ecx}" (x));

Although the hard register constraint refers to register ecx, the actual register will be rcx since on x86_64 a long is 8 byte in total. This aligns with register asm where you could have

register long x asm ("ecx");

Interaction with Register asm

A mixture of both constructs as for example

register int x asm ("r4") = 42;
int y;
asm ("" : "={r5}" (y) : "r" (x));

is valid.

If an operand is a register asm and the corresponding constraint a hard register, then both must refer to the same register. That means

register int x asm ("r4");
asm ("" : "={r4}" (x));

is valid and

register int x asm ("r4");
asm ("" : "={r5}" (x));  // invalid

is invalid.

Note, register asm may not only be clobbered by function calls but also by inline asm in conjunction with hard register constraints. For example, in the following

register int x asm ("r5") = 42;
int y;
asm ("" : "={r5}" (y));
asm ("" : "+r" (x));

variable x materializes before the very first inline asm which writes to register r5 and therefore clobbers x which in turn is read by the subsequent inline asm.

Limitations

At the moment fixed registers are not supported for hard register constraints. Thus, idioms like

register void *x asm ("rsp");
asm ("" : "=r" (x));

are not supported for hard register constraints. This might be lifted.