gcc的primefaces操作和代码生成

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

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关于代码生成的primefaces武器谈话,他提到X86手册要求将xchg用于primefaces存储,并使用简单的mov来进行primefaces读取。 所以我期待的是:

 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 

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

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

问题的例子是做一个primefaces商店,而不是交换。 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体系结构中,必须使用显式内存屏障或primefaces指令(如示例中所示)。 在某些系统上,例如IA-64,有一些特殊的“解锁”指令可以提供所需的内存排序。

内存屏障mfence确保所有存储都已完成(CPU内核中的存储缓冲区为空并且值存储在缓存或内存中),它还确保将来的加载不会无序执行。

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

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

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