【发布时间】:2021-06-11 21:16:22
【问题描述】:
众所周知,asm volatile ("" ::: "memory") 可以用作编译器屏障,以防止编译器在其中重新排序汇编指令。例如,在https://preshing.com/20120625/memory-ordering-at-compile-time/,“显式编译器障碍”一节中提到。
但是,我能找到的所有文章都只提到asm volatile ("" ::: "memory") 可以充当编译器屏障,而没有说明"memory" clobber 可以有效地形成编译器屏障的原因。 GCC online documentation 只说所有特殊的破坏者"memory" 所做的就是告诉编译器汇编代码可能会执行除操作数列表中指定的内存读取或写入之外的内存读取或写入。但是这样的语义是如何导致编译器停止对内存指令重新排序的尝试呢?我试图回答自己但失败了,所以我在这里问:为什么asm volatile ("" ::: "memory") 可以作为编译器屏障,基于"memory"clobber 的语义?请注意,我问的是“编译器屏障”(在编译时有效),而不是更强的“内存屏障”(在运行时有效)。为方便起见,我在下面的 GCC 在线文档中摘录了"memory"clobber 的语义:
"memory"clobber 告诉编译器汇编代码 对未列出的项目执行内存读取或写入 输入和输出操作数(例如,访问内存 由输入参数之一指向)。确保内存包含 正确的值,GCC 可能需要刷新特定的寄存器值以 执行asm之前的内存。此外,编译器不假设 在asm之前从内存中读取的任何值在之后保持不变 那asm;它会根据需要重新加载它们。使用"memory"clobber 有效地为编译器形成了一个读/写内存屏障。
【问题讨论】:
-
如果指令的效果未知,您不能重新排序。
-
IMO 它告诉编译器在下一条语句之前完成所有内存(以及其他操作)。就像在这个例子中:godbolt.org/z/fKv6GaGET
-
我想你在一两天前问了一个非常相似的问题,和它一样,我不明白为什么引用的段落还没有回答这个问题。您能否举一个可能的重新排序的具体示例,您不确定引用的文本是否或为什么禁止它?比如说,C 源代码的 sn-p 以及您不确定编译器是否可以发出的汇编(伪)代码。那么可能有人可以更具体地解释一下。
-
@Nate Eldredge:您看不到它,因为您是专家,并且您认为这两个问题出于相同的原因非常相似。但是直到刚才我还没有看到 reads-everything / writes-everything 效果与编译器屏障效果的相似性——它们都是“要求内存同步的理想效果”。正是“同步”这句话让我想起了preshing.com/20120710/… 中的类比,然后我一下子就明白了你在评论中提到的相似之处。
标签: c gcc inline-assembly volatile memory-barriers