【发布时间】:2012-11-12 14:20:26
【问题描述】:
如下:
#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
) ;
}
我得到一个编译错误:
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'
(来自 g++ 或 clang++ 的相同类型的错误)
这让我很吃惊,因为我在 gcc 文档的 i386 clobbers 中看到了 a、b、c、d 列
http://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints
我可以通过显式来修复clobber约束:
"%rax", "%rbx", "%rcx", "%rdx" // clobbered registers
但我很惊讶我必须这样做?我只需要它在 x86_64 上工作,但我认为“a”、“b”、“c”、“d”样式约束会更好,以防以后在 i386 上也需要代码。
编辑:我最初发布了错误的 asm,经过几次调整以使其正常工作,并在此过程中搞混了。上面的 asm 与我最初的问题一致,但是会导致编译器无法调度 A 寄存器的编译错误。这似乎可行:
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
) ;
}
但请注意,它完全避免了任何 clobber 约束的要求,因为所有 clobber 都是输出。这可能是正确的方法,尽管在阅读 gcc 文档后我仍然感到惊讶,我不能在 clobber 约束中使用通用的 reg 名称“a”、“b”、“c”、“d”,而不是必须使用 "%eax", "%rax", ...
【问题讨论】:
-
根据 PIC 的使用和内存模型,您可能需要保存
EBX/RBX,因为根据 ABI 规范,调用者拥有它。"=b"(p->b)破坏EBX/RBX。 -
如果我将 rbx 列为输出,编译器是否不必为 -fpic 本身进行溢出(而不是我必须明确地这样做)?
标签: gcc clang inline-assembly