1

以下では:

#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クロバーにa、b、c、dがリストされているので、これは私を驚かせます

http://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html#Machine-Constraints

明示的にすることで、clobber 制約を修正できます。

"%rax", "%rbx", "%rcx", "%rdx"                  // clobbered registers

しかし、私はそうしなければならないことに驚いていますか?これは x86_64 で動作するために必要なだけですが、コードが後で i386 でも必要になった場合に備えて、"a"、"b"、"c"、"d" スタイルの制約の方が適切だと思いました。

編集: 最初に間違った 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
           ) ;
}

ただし、すべてのクロバーは出力であるため、クロバー制約の要件をまったく回避していることに注意してください。それはおそらくそれを行う正しい方法ですが、gcc ドキュメントを読んだ後でも、ジェネリック reg 名「a」、「b」、「c」、「d」を clobber 制約で使用できないことにまだ驚いています。代わりに、「%eax」、「%rax」などを使用する必要があります...

4

2 に答える 2

1

gccドキュメントのi386クロバーにa、b、c、dがリストされているのを見て、これは私を驚かせます

クロバーは制約ではありません。

制約は、GCC に insn オペランドにレジスタを割り当てるように指示するときに使用されます。制約は、レジスタを引き出す許容可能なレジスタ クラスを定義します。

一方、クロバーは、入力/出力の制約から明らかでない場合に、insn によって変更されるレジスターについて GCC に通知します。または、インライン アセンブリでハードコーディングされたレジスタ名を使用する場合。

これが必要なのは、インライン 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
于 2012-11-24T18:20:08.323 に答える
0

次の GCC 4.8 リリースを待つことができる場合、または GCC の最新のスナップショットを使用できる場合 (つまり、svn ソースからトランクをコンパイルする場合)、新しいビルトイン __builtin_cpu_is__builtin_cpu_supports;の使用を検討してください。

それ以外の場合は、提案したとおりに実行してください。たとえば、clobber 制約を明示的に指定してください。

注意:参照するリンクは、4.7以前ではなく、将来のGCC 4.8用でもあります

于 2012-11-24T14:52:57.003 に答える