问题 gcc的原子操作和代码生成


我正在考虑通过gcc查看为原子操作生成的一些程序集。我尝试了以下短序列:

int x1;
int x2;

int foo;

void test()
{
  __atomic_store_n( &x1, 1, __ATOMIC_SEQ_CST );
  if( __atomic_load_n( &x2  ,__ATOMIC_SEQ_CST ))
    return;

  foo = 4;
}

看看Herb Sutter关于代码生成的原子武器谈话,他提到X86手册要求使用 xchg 对于原子商店和一个简单的 mov 用于原子读取。所以我期待的是:

test():
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $1, %eax
    xchg    %eax, x1(%rip)
    movl    x2(%rip), %eax
    testl   %eax, %eax
    setne   %al
    testb   %al, %al
    je      .L2
    jmp     .L1
.L2:
    movl    $4, foo(%rip)
.L1:
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

由于锁定,内存栅栏是隐含的 xchg 指令。

但是,如果我使用编译它 gcc -march=core2 -S test.cc 我得到以下内容:

test():
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $1, %eax
    movl    %eax, x1(%rip)
    mfence
    movl    x2(%rip), %eax
    testl   %eax, %eax
    setne   %al
    testb   %al, %al
    je      .L2
    jmp     .L1
.L2:
    movl    $4, foo(%rip)
.L1:
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

所以不要使用 xchg 操作gcc这里用的是 mov + mfence 组合。这个代码生成的原因是什么,与Herb Sutter根据x86架构规定的代码生成不同?


7752
2018-03-09 13:18


起源



答案:


xchg 当目标是内存位置时,指令隐含了锁定语义。这意味着您可以原子地将寄存器的内容与内存位置的内容进行交换。

问题的例子是做一个原子商店,而不是交换。 x86架构内存模型保证在多处理器/多核系统中,由一个线程完成的存储将由其他线程按顺序看到......因此内存移动就足够了。话虽如此,有较旧的Intel CPU和一些克隆,这个区域有bug,而且有 xchg 需要作为这些CPU的解决方法。请参阅此维基百科有关自旋锁的文章的重要优化部分:

http://en.wikipedia.org/wiki/Spinlock#Example_implementation

哪个州

上面的简单实现适用于使用x86架构的所有CPU。但是,可以进行许多性能优化:

在x86架构的后续实现中,spin_unlock可以安全地使用解锁的MOV而不是较慢的锁定XCHG。这是由于微妙的内存排序规则支持这一点,即使MOV不是一个完整的内存屏障。但是,某些处理器(一些Cyrix处理器,一些Intel Pentium Pro修订版(由于错误)以及早期的Pentium和i486 SMP系统)将做错事,受锁保护的数据可能会被破坏。在大多数非x86体系结构中,必须使用显式内存屏障或原子指令(如示例中所示)。在某些系统上,例如IA-64,有一些特殊的“解锁”指令可以提供所需的内存排序。

记忆障碍, mfence,确保所有商店都已完成(CPU核心中的存储缓冲区为空,值存储在缓存或内存中),它还确保未来的加载不会无序执行。

在1999年,英特尔架构师对Linus Torvalds的回复“正式”阐明了MOV足以解锁互斥锁(无需序列化或内存障碍)的事实

http://lkml.org/lkml/1999/11/24/90

我想后来发现它不适用于一些旧的x86处理器。


9
2018-03-09 13:55