In the following:
#include <string.h>
struct cpuidOut
{
long a ;
long b ;
long c ;
long d ;
} ;
void callcpuid( cpuidOut * p, long a )
{
memset( p, 0xFF, sizeof(*p) ) ;
p->a = a ;
__asm__ ( "cpuid"
: "+a"(p->a), "=b"(p->b), "=c"(p->c), "=d"(p->d) // output
: // no (only) inputs
: "a", "b", "c", "d" // clobbered registers
) ;
}
I get a compile error:
t.C:22: error: unknown register name 'd' in 'asm'
t.C:22: error: unknown register name 'c' in 'asm'
t.C:22: error: unknown register name 'b' in 'asm'
t.C:22: error: unknown register name 'a' in 'asm'
(same sort of error from either g++ or clang++)
This suprises me since I see a, b, c, d listed in the i386 clobbers in the gcc documentation
http://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints
I can fix the clobber constraints by being explicit:
"%rax", "%rbx", "%rcx", "%rdx" // clobbered registers
but I’m suprised I have to do so? I only need this to work on x86_64, but I thought the “a”, “b”, “c”, “d” style constraints would be nicer in case the code is later needed on i386 too.
EDIT: I’d initially posted the wrong asm after tweaking it a couple of times trying to get it to work, and getting mixed up along the way. The asm above is consistent with my initial question, however, causes a compile error with the compiler unable to schedule the A register. This seems to work instead:
void callcpuid( cpuidOut * p, long a, long b )
{
__asm__ ( "cpuid"
: "=a"(p->a), "=b"(p->b), "=c"(p->c), "=d"(p->d) // output
: "0"(a), "1"(b) // inputs
) ;
}
but notice that it avoids the requirement for any clobber constraints at all, since all the clobbers are outputs. That’s probably the right way to do it, although I’m still suprised after reading the gcc docs that I can’t use the generic reg names “a”, “b”, “c”, “d” in the clobber constraints, instead having to use “%eax”, “%rax”, …
The clobbers are not constraints.
Constraints are used when you tell GCC to allocate registers for insn operands, the constraints define the acceptable register class from which to draw registers.
The clobbers, on the other hand, tell GCC about registers, which are modified by the insns, in the cases where it’s not evident from the input/output constraints, for example an insn, which modifies a fixed register, which is not one of its operands or when you use hardcoded register names in the inline assembly.
This is needed, so before execution of the inline asm, GCC can stash away values, that it happens to keep in the clobbered registers.
PS. For input-output operands, you can used the
"+"modifier:PS. 32-bit code generated:
64-bit code generated: