88

在实现无锁数据结构和时序代码时,通常需要抑制编译器的优化。通常人们会在 clobber 列表中使用asm volatilewith来执行此操作memory,但有时您只会看到asm volatile或只是一个普通的asmclobbering 内存。

这些不同的语句对代码生成有什么影响(特别是在 GCC 中,因为它不太可能是可移植的)?

仅供参考,这些是有趣的变化:

asm ("");   // presumably this has no effect on code generation
asm volatile ("");
asm ("" ::: "memory");
asm volatile ("" ::: "memory");
4

3 回答 3

72

请参阅GCC 文档中的“扩展 Asm”页面

您可以通过在 . 后面写关键字来防止asm指令被删除。[...]关键字表明该指令具有重要的副作用。如果 asm 可访问,GCC 不会删除它。volatileasmvolatilevolatile

没有任何输出操作数的asm指令将被视为与 volatileasm指令相同。

您的示例都没有指定输出操作数,因此asmasm volatile表单的行为相同:它们在代码中创建了一个不能被删除的点(除非它被证明是不可访问的)。

这不等于什么都不做。有关更改代码生成的虚拟示例,请参阅此问题asm- 在该示例中,循环 1000 次的代码被矢量化为一次计算 16 次循环迭代的代码;asm但是循环内部的存在会抑制优化(asm必须达到 1000 次)。

clobber使"memory"GCC 假定任何内存都可以被asm块任意读取或写入,因此将阻止编译器重新排序加载或存储:

这将导致 GCC 不会在整个汇编器指令中将内存值缓存在寄存器中,并且不会优化对该内存的存储或加载。

(但这并不能阻止 CPU 相对于另一个 CPU 重新排序加载和存储;您需要真正的内存屏障指令。)

于 2013-01-22T01:10:20.040 回答
13

asm ("")什么都不做(或者至少,它不应该做任何事情。

asm volatile ("")也什么都不做。

asm ("" ::: "memory")是一个简单的编译器围栏。

asm volatile ("" ::: "memory")AFAIK 与以前的相同。volatile关键字告诉编译器不允许移动这个汇编块。例如,如果编译器确定每次调用中的输入值都相同,它可能会被提升出循环。我不确定在什么条件下编译器会决定它对程序集有足够的了解以尝试优化其位置,但volatile关键字完全抑制了这一点。也就是说,如果编译器试图移动一个asm没有声明输入或输出的语句,我会感到非常惊讶。

顺便说一句,volatile如果编译器确定输出值未使用,它也会阻止编译器删除表达式。这只有在有输出值的情况下才会发生,所以它不适用于asm ("" ::: "memory").

于 2013-01-21T23:41:58.587 回答
3

只是为了Lily Ballard 的回答的完整性,Visual Studio 2010 提供了_ReadBarrier()_WriteBarrier()并且_ReadWriteBarrier()做同样的事情(VS2010 不允许 64 位应用程序的内联汇编)。

这些不会生成任何指令,但会影响编译器的行为。一个很好的例子是here

MemoryBarrier()生成lock or DWORD PTR [rsp], 0

于 2013-01-22T00:55:41.823 回答