[英]What is the life-time of asm volatile(“” ::: “memory”)?
我还阅读了许多有关编译障碍和内存障碍的解释,尽管我还不确定编译器如何知道防止编译内存排序的起点和终点。 (另一方面,我了解cpu内存屏障的工作原理...)
下面是一个任意示例,没有编译障碍。
int func(int *x, int *y) {
int var = 0;
x[0] += 1;
var += y[0];
y[0] += 1;
x[1] += 1;
var += y[1];
y[0] += 1;
return var;
}
例如,如果我想仅在此函数中而不是在其他函数中阻止编译存储器排序,是否应该在返回var之前将asm volatile(“” :::“ memory”)插入函数的末尾?
喜欢:
int func(int *x, int *y) {
int var = 0;
x[0] += 1;
var += y[0];
y[0] += 1;
x[1] += 1;
var += y[1];
y[0] += 1;
asm volatile("" ::: "memory");
return var;
}
屏障可防止您在任何地方放置重新排序(或优化)。 没有神奇的“范围”。 只需看一下内联汇编指令即可 :
asm volatile (""::: "memory");
volatile
关键字的意思是将asm
语句准确放置在我放置的位置,而不要对其进行优化(即删除它)。 在第三个之后:
是clobbers的列表,因此这意味着“我已经破坏了内存”。 您基本上是在告诉编译器“我做了一些影响内存的事情”。
在您的示例中,您有类似
y[0] += 1;
y[0] += 1;
编译器非常聪明,并且知道这样做效率不高。 它可能会将其编译成类似
load y[0] from memory to register add 2 to this register store result to y[0]
由于流水线的原因,将其与其他加载/修改/存储操作结合起来可能也会更有效。 因此,编译器可能会将其与附近的操作合并,从而进一步对其重新排序。
为了防止这种情况,可以在它们之间放置一个内存屏障:
y[0] += 1;
asm volatile (""::: "memory");
y[0] += 1;
这告诉编译器在第一条指令之后,“我已经对内存做了一些操作,您可能不知道,但是它确实发生了。” 因此,它不能使用其标准逻辑,并假设在同一存储位置加一两次与向其添加两个相同,因为两者之间发生了某些情况。 所以这将被编译成更像
load y[0] from memory to register add 1 to this register store result to y[0] load y[0] from memory to register add 1 to this register store result to y[0]
同样,它可能会重新排列障碍物两侧的事物,但不能跨越障碍物。
另一个例子:一次,我在微控制器上使用内存映射I / O。 编译器发现我正在将不同的值写入同一地址,而没有中间的读取,因此它将其优化为一次写入最后一个值。 当然,这使我的I / O活动无法按预期进行。 在两次写入之间放置一个内存屏障会告诉编译器不要这样做。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.