asm,asm volatile和clobbering memory之间的区别
在实现无锁数据结构和时间代码时,往往需要抑制编译器的优化。 通常情况下,人们在clobber列表中使用asm volatile
和memory
来做这件事,但是有时你会看到只是asm volatile
或只是一个简单的asm
clobbering memory。
这些不同的语句对代码生成有什么影响(特别是在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
指令将被视为易失性asm
指令。
你的例子都没有指定输出操作数,所以asm
和asm volatile
表单的行为是相同的:它们在代码中创build一个不能被删除的点(除非被certificate是不可访问的)。
这与无所事事并不完全相同。 看到这个问题的一个虚拟asm
,改变代码生成的例子 – 在这个例子中,循环1000次的代码被vector化成代码,它一次计算循环的16次迭代; 但是在循环内存在一个asm
抑制优化( asm
必须达到1000次)。
"memory"
clobber使GCC认为任何内存都可以由asm
块任意读取或写入,这样可以防止编译器重新sorting加载或存储:
这将导致GCC不将内存值保存在汇编指令中的寄存器中,也不会优化存储或加载到该内存。
(虽然这并不妨碍CPU对另一个CPU的加载和存储进行重新sorting,但是您需要真正的内存屏障指令。)
asm ("")
什么都不做(或者至less,它不应该做任何事情。
asm volatile ("")
也什么都不做。
asm ("" ::: "memory")
是一个简单的编译器篱笆。
asm volatile ("" ::: "memory")
AFAIK和前面一样。 volatile
关键字告诉编译器不允许移动这个汇编块。 例如,如果编译器确定input值在每个调用中都是相同的,则可以将其从循环中提出。 我不太确定在什么条件下,编译器会认为它对组件有足够的了解来优化它的位置,但是volatile
关键字完全抑制了这一点。 也就是说,如果编译器试图移动一个没有声明的input或输出的asm
语句,我会感到非常惊讶。
顺便说一句, volatile
还会阻止编译器在决定输出值未被使用时删除该expression式。 这只会发生,如果有输出值虽然,所以它不适用于asm ("" ::: "memory")
。
为了完整起见,Kevin Ballard的回答是,Visual Studio 2010提供_ReadBarrier(),_WriteBarrier()和_ReadWriteBarrier()来做同样的事情(VS2010不允许为64位应用程序内联汇编)。
这些不会生成任何指令,但会影响编译器的行为。 这里有一个很好的例子
MemoryBarrier()生成lock or DWORD PTR [rsp], 0