2011-07-20 12 views
9

ia32のCMPXCHG8B用にGCCインラインasmを作成しようとしています。いいえ、私は__sync_bool_compare_and_swapを使用できません。 -fPICの有無にかかわらず動作する必要があります。GCCインラインアセンブリーでCMPXCHG8Bを32ビットでラップする正しい方法

これまでのところ、私がした最高(EDIT:詳細については、以下の私自身の答えを見、結局は動作しませんが)

register int32 ebx_val asm("ebx")= set & 0xFFFFFFFF; 
asm ("lock; cmpxchg8b %0;" 
    "setz %1;" 
    : "+m" (*a), "=q" (ret), "+A" (*cmp) 
    : "r" (ebx_val), "c" ((int32)(set >> 32)) 
    : "flags") 

で、これは実際に正しいかどうか、私はよく分からないが。

PICのためにebx_valのために"b" ((int32)(set & 0xFFFFFFFF))を実行できませんが、明らかにregister asm("ebx")変数がコンパイラによって受け入れられます。

BONUS:RET変数が分岐に使用し、そのコードは次のように見て終わるさ:

cmpxchg8b [edi]; 
setz cl; 
cmp cl, 0; 
je foo; 

それがなるように、出力オペランドを記述するためにどのように任意のアイデア:

cmpxchg8b [edi] 
jz foo 

ありがとうございます。

+3

コンパイラ組み込み関数は-fPICでは動作しないという事実は、ちょうどフラットアウトコンパイラのバグ次のとおりです。http: //gcc.gnu.org/bugzilla/show_bug.cgi?id=37651 壊れたコンパイラを回避する必要がある場合は、そのバグのCCリストに載せたいと思うかもしれません。 – Crashworks

+0

IA32で '-fPIC'を使用しますか?なぜ私は不思議です。 – Gabe

+4

@Gabe - 共有ライブラリオブジェクトを書くときに最も重要です。 Ulrich Drepper氏は、このテーマに関する優れた論文を持っています。http://www.akkadia.org/drepper/dsohowto.pdf – Crashworks

答えて

2

どのくらい小さいテストで私のために働くように思わ以下、およそ:これも正しくコンパイルされます

int sbcas(uint64_t* ptr, uint64_t oldval, uint64_t newval) 
{ 
    int changed = 0; 
    __asm__ (
     "push %%ebx\n\t" // -fPIC uses ebx, so save it 
     "mov %5, %%ebx\n\t" // load ebx with needed value 
     "lock\n\t" 
     "cmpxchg8b %0\n\t" // perform CAS operation 
     "setz %%al\n\t" // eax potentially modified anyway 
     "movzx %%al, %1\n\t" // store result of comparison in 'changed' 
     "pop %%ebx\n\t" // restore ebx 
     : "+m" (*ptr), "=r" (changed) 
     : "d" ((uint32_t)(oldval >> 32)), "a" ((uint32_t)(oldval & 0xffffffff)), "c" ((uint32_t)(newval >> 32)), "r" ((uint32_t)(newval & 0xffffffff)) 
     : "flags", "memory" 
     ); 
    return changed; 
} 

場合は、この動作をトリガーする小さなスニペットを含めてくださいだろうか?

cmpxchg8b命令の条件コードを使用してアセンブラブロックの後に分岐することはできません(asm gotoなどの機能を使用しない限り)。GNU C Language Extensionsから

アセンブラ命令によって残された条件コードにアクセスする方法を探すのは自然な考えです。しかし、これを実装しようとしたとき、確実に動作させる方法が見つけられませんでした。問題は、出力オペランドがリロードを必要とする可能性があり、その結果、追加の「ストア」命令が続くことになります。ほとんどのマシンでは、これらの命令はテストする前に条件コードを変更します。この問題は、出力オペランドを持たないため、通常の「テスト」命令と「比較」命令では発生しません。

編集:私はまた%N入力値を使用している間、スタックを変更するためにOKであるかどうかを一つの方法または他のを指定する任意のソースを見つけることができません(This古代のリンクは、あなたも、上にあなたのレジスタをプッシュすることができます」と言いますスタックを作成して使用して戻してください。」という例ですが、この例では入力がありません)。

しかし、他のレジスタに値を固定することによりずに行うことが可能でなければなりません:

int sbcas(uint64_t* ptr, uint64_t oldval, uint64_t newval) 
{ 
    int changed = 0; 
    __asm__ (
     "push %%ebx\n\t" // -fPIC uses ebx 
     "mov %%edi, %%ebx\n\t" // load ebx with needed value 
     "lock\n\t" 
     "cmpxchg8b (%%esi)\n\t" 
     "setz %%al\n\t" // eax potentially modified anyway 
     "movzx %%al, %1\n\t" 
     "pop %%ebx\n\t" 
     : "+S" (ptr), "=a" (changed) 
     : "0" (ptr), "d" ((uint32_t)(oldval >> 32)), "a" ((uint32_t)(oldval & 0xffffffff)), "c" ((uint32_t)(newval >> 32)), "D" ((uint32_t)(newval & 0xffffffff)) 
     : "flags", "memory" 
     ); 
    return changed; 
} 
+1

ありがとう!私が見ることのできる1つの問題は、スニペットと同じです。コンパイラはESPを通して%0をアドレス指定し、ESPがプッシュ/ポップによって変更されたことを伝えることはできません。また、情報ありがとうございます。出力の条件コードは、私が疑ったものを確認しています。 –

+0

私はGCCがこれをやっているのを見たことはないと思う。それは私が見た限りでは、いつも "ebp"を通してそれを行います。これがいつも保証されているかどうか(またはそうすることができるかどうか)について、私が参照を掘り下げることができるかどうかがわかります。 – user786653

+0

私はiccがそうする参考文献を見ました。 –

2

これは私が持っているものです。

bool 
spin_lock(int64_t* lock, int64_t thread_id, int tries) 
{ 
    register int32_t pic_hack asm("ebx") = thread_id & 0xffffffff; 
retry: 
    if (tries-- > 0) { 
     asm goto ("lock cmpxchg8b %0; jnz %l[retry]" 
        : 
        : "m" (*lock), "A" ((int64_t) 0), 
        "c" ((int32_t) (thread_id >> 32)), "r" (pic_hack) 
        : 
        : retry); 
     return true; 
    } 
    return false; 
} 

それはC・ラベルにインラインアセンブリからジャンプすることができますgccの4.5での新機能asm gotoを、使用しています。 (ああ、古いバージョンのgccをサポートしなければならないというあなたのコメントを参照してください)ああ、私は試しました:--P)

+0

ありがとう!私のコードはあなたのものと似ています私はasm gotoを使用することはできません。しかし、いくつかの質問:1)なぜ*ロックオペランドは入力専用で、入出力はできませんか? 2)EFLAGSがclobberedレジスタリストにない理由 –

+0

@Laurynas:1. 'asm goto'は出力制約を持つことができません(現在の制限はgccの後のバージョンでは削除される可能性があります)。私はロックの現在の値を気にしないので(私たちは再帰的なロックを試みるつもりはありません;-))、それは受け入れられました。 2. asm gotoの例にはそれがないので(条件付きジャンプも)、asm gotoはデフォルトでclobberedフラグを仮定します。 –

+0

将来の読者のために、x86とx86-64用のgccのマシン定義では、すべてのインラインasm文に暗黙的に '' cc "' clobberが含まれています。 x86 asmに対して明示的に書く必要はありません。 –

1

驚くことではありませんが、 EBXレジスタがregister asmに設定される前に、EBX(PIC)を使用して間接的にアドレス指定可能なasmオペランドは、set & 0xFFFFFFFFに割り当てられた後にgccがEBXを介してオペランドをロードします。

これは私が今の仕事をしようとしていたコードである:(EDIT:プッシュ/ポップを避ける)

asm ("movl %%edi, -4(%%esp);" 
    "leal %0, %%edi;" 
    "xchgl %%ebx, %%esi;" 
    "lock; cmpxchg8b (%%edi);" // Sets ZF 
    "movl %%esi, %%ebx;"  // Preserves ZF 
    "movl -4(%%esp), %%edi;" // Preserves ZF 
    "setz %1;"     // Reads ZF 
    : "+m" (*a), "=q" (ret), "+A" (*cmp) 
    : "S" ((int32)(set & 0xFFFFFFFF)), "c" ((int32)(set >> 32)) 
    : "flags") 

ここでの考え方は、任意の間接アドレッシングしばらく避けるためにも、EBXをつかう前にオペランドをロードすることですCMPXCHG8BのEBX値を設定します。私はハード・レジスタESIをオペランドの下半分について修正します。もしそうでなければ、GCCは、値が等しいことを証明できれば、既に他のレジスタを再利用することができます。 EDIレジスタは手動で保存されます。これは、おそらくレジスタ圧が高いために、「不可能なリロード」を持つclobberedレジスタリストのチョークGCCに追加するだけです。他のオペランドがESPアドレス指定されている可能性があるため、PUSH/POPはEDIの保存時には避けられます。

関連する問題