asm,asm volatile和clobbering memory之间的区别

时间:2013-01-21 23:35:52

标签: c gcc inline-assembly

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

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

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

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

3 个答案:

答案 0 :(得分:52)

请参阅"Extended Asm" page in the GCC documentation

  

您可以在asm之后写一个关键字volatile来阻止asm指令被删除。 [...] volatile关键字表示该指令具有重要的副作用。如果可以访问,GCC将不会删除volatile asm。

  

没有任何输出操作数的asm指令将被视为与易失性asm指令完全相同。

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

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

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

  

这将导致GCC不在汇编器指令的寄存器中保持缓存的内存值,也不会优化存储器或加载到该存储器。

(但这并不妨碍CPU对另一个CPU重新排序加载和存储;你需要真正的内存屏障指令。)

答案 1 :(得分:7)

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

asm volatile ("")也什么也没做。

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

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

顺便说一句,volatile还会阻止编译器在确定输出值未使用时删除该表达式。只有在存在输出值时才会发生这种情况,因此它不适用于asm ("" ::: "memory")

答案 2 :(得分:1)

为了完整解决Kevin Ballard的问题,Visual Studio 2010提供了_ReadBarrier(),_ _ WriteBarrier()和_ReadWriteBarrier()来做同样的事情(VS2010不允许64位应用程序的内联汇编)。

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

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