rax,rbx的x86_64 gcc内联汇编约束,

时间:2012-11-24 14:44:34

标签: gcc clang inline-assembly

以下内容:

#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”,...

2 个答案:

答案 0 :(得分:1)

  

这让我很吃惊,因为我在gcc文档中看到了i386 clobbers中列出的a,b,c,d

clobbers不是约束。

当你告诉GCC为insn操作数分配寄存器时使用约束,约束定义了可以从中绘制寄存器的可接受寄存器类。

另一方面,如果输入/输出约束不明显,例如insn修改固定寄存器,那么另一方面,clobbers告诉GCC关于寄存器的修改,这些寄存器由insn修改。不是它的一个操作数,或者在内联汇编中使用硬编码的寄存器名称。

这是必需的,所以在执行内联asm之前,GCC可以隐藏值,它恰好保留在被破坏的寄存器中。

PS。对于输入输出操作数,您可以使用"+"修饰符:

void callcpuid( cpuidOut * p, long a, long b )
{
   __asm__ ( "cpuid" : "+a"(p->a), "+b"(p->b), "=c"(p->c), "=d"(p->d)) ;
}

PS。生成32位代码:

movl    (%esi), %eax  ; load p->a
movl    4(%esi), %ebx ; load p->b
cpuid
movl    %ebx, 4(%esi)  ; write back into p->b
movl    (%esp), %ebx   
movl    %eax, (%esi)   ; write back into p->a
movl    %ecx, 8(%esi)  ; write p->c
movl    %edx, 12(%esi) ; write p->d

生成64位代码:

movq    (%rdi), %rax   ; load p->a
movq    8(%rdi), %rbx  ; load p->b
cpuid
movq    %rbx, 8(%rdi)  ; write back p->b
movq    %rax, (%rdi)   ; write back p->a
movq    %rcx, 16(%rdi) ; write p->c
movq    %rdx, 24(%rdi) ; write p->d

答案 1 :(得分:0)

如果您可以等待下一个GCC 4.8版本,或者您可以使用GCC的最新快照(即从svn源编译中继),请考虑使用新的builtins __builtin_cpu_is和{ {1}};

否则,按照你的建议行事,例如:用clobber约束明确。

注意:您所引用的link也适用于未来的GCC 4.8,而不适用于4.7或更早版本