[英]The difference between asm, asm volatile and clobbering memory
在实现无锁数据结构和时序代码时,通常需要抑制编译器的优化。 通常人们在clobber列表中使用asm volatile
with memory
来做这件事,但是你有时会看到asm volatile
或者仅仅是一个简单的asm
clobbering内存。
这些不同的陈述对代码生成有什么影响(特别是在GCC中,因为它不太可能是可移植的)?
仅供参考,这些是有趣的变化:
asm (""); // presumably this has no effect on code generation
asm volatile ("");
asm ("" ::: "memory");
asm volatile ("" ::: "memory");
请参阅GCC文档中的“Extended Asm”页面 。
您可以通过在
asm
之后写入关键字volatile
来防止删除asm
指令。 [...]volatile
关键字表示该指令具有重要的副作用。 如果可以访问,GCC不会删除volatile
asm。
和
没有任何输出操作数的
asm
指令将被视为与volatileasm
指令完全相同。
您的示例都没有指定输出操作数,因此asm
和asm volatile
表单的行为相同:它们在代码中创建一个可能不会被删除的点(除非它被证明是无法访问的)。
这与什么都不做完全不一样。 请参阅此问题 ,以获取更改代码生成的虚拟asm
示例 - 在该示例中,循环1000次循环的代码被矢量化为代码,该代码一次计算循环的16次迭代; 但是在循环内存在asm
抑制优化( asm
必须达到1000次)。
"memory"
崩溃使得GCC假设任何内存都可以由asm
块任意读取或写入,因此会阻止编译器在其上重新排序加载或存储:
这将导致GCC不保持跨汇编指令缓存在寄存器中的内存值,而不是优化存储器或加载到该内存。
(但这并不妨碍CPU相对于另一个CPU重新排序加载和存储;您需要真正的内存屏障指令。)
asm ("")
什么都不做(或者至少,它不应该做任何事情。
asm volatile ("")
也什么也没做。
asm ("" ::: "memory")
是一个简单的编译器围栏。
asm volatile ("" ::: "memory")
AFAIK与之前相同。 volatile
关键字告诉编译器不允许移动此程序集块。 例如,如果编译器确定每次调用中的输入值相同,则可以将其从循环中提升。 我不确定在什么条件下编译器会决定它对程序集的了解程度足以尝试优化其位置,但volatile
关键字完全抑制了这一点。 也就是说,如果编译器试图移动没有声明输入或输出的asm
语句,我会非常惊讶。
顺便说一句, volatile
也会阻止编译器在确定输出值未使用时删除它。 这只有在有输出值时才会发生,所以它不适用于asm ("" ::: "memory")
。
仅仅为了完整了Lily Ballard的回答 ,Visual Studio 2010提供了_ReadBarrier()
, _WriteBarrier()
和_ReadWriteBarrier()
来做同样的事情(VS2010不允许64位应用程序的内联汇编)。
这些不会生成任何指令,但会影响编译器的行为。 一个很好的例子是在这里 。
MemoryBarrier()
生成lock or DWORD PTR [rsp], 0
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.