cmpxchg を GCC 拡張で

http://0xcc.net/blog/archives/000128.html

を見て、なんかこの手の命令はたいてい GCC 拡張にあるんだよなーとか思ってたらありました。 __sync_bool_compare_and_swap と __sync_val_compare_and_swap 。

#include <stdio.h>

void once(void) {
  // int は atomic な読み書きが可能
  static volatile int entered; // 最初は 0
  int result;
  result = __sync_val_compare_and_swap(&entered, 0, 1);  // cmpxchg になる

  if (result == 1) {  // すでに入ったことがある場合は
    return;  // すぐ出る
  }
  // 初回の場合のみ、何かを実行する
  puts("once");
}

int main() {
    once();
    once();
    return 0;
}

コンパイルgcc -march=i486 once.c などと、 -march=i486 とかをつけて下さい。 386 だと見つからないとか怒られる…というか、それで結構な時間探しまわってました。ちなみに GCC-4.0.2 とかで見当たらなかったので、たぶん GCC-4.1 以降です。 x86 だけで試してたらあんま意味ないので、 Alpha でも上記コードが動くことを確認しておきました(本当に同時実行で大丈夫なのかとか調べてませんが)。見た感じ alpha と i386 と ia64 と rs6000 と s390 で動きそうです (config/*/*.md で sync_compare で grep しただけ) 。

生成されてるコードは以下みたいな。

once:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    $0, %eax
        movl    $1, %edx
        lock
        cmpxchgl        %edx, entered.1778
        movl    %eax, -4(%ebp)
        cmpl    $1, -4(%ebp)
        je      .L4
        subl    $12, %esp
        pushl   $.LC0
        call    puts
        addl    $16, %esp
.L4:
        leave
        ret

あ、ちなみに __builtin プレフィクスがついてないのは Itanium の ABI にあわせるためとか書いてありました。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h