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.
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");
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.
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.