asm volatile(“”:::“记忆”)的生命周期是什么?

时间:2016-02-06 20:35:14

标签: c gcc memory embedded

我已经阅读了许多关于编译障碍和内存障碍的解释,但我还不确定编译器如何知道编译内存排序的阻止在何处开始以及在何处结束。 (另一方面,我理解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;
}

1 个答案:

答案 0 :(得分:1)

屏障可以防止您在任何地方重新排序(或优化)。没有神奇的范围"。只需查看inline assembly instruction

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]

由于pipelining原因,将其与其他加载/修改/存储操作相结合可能更有效。因此,编译器可以通过将其与附近的操作合并来进一步重新排序。

为了防止这种情况,您可以在它们之间放置一个内存屏障:

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活动无法按预期工作。在写入之间设置内存屏障告诉编译器不要这样做。

相关问题