asm volatile("" ::: "memory");
创建编译器级内存屏障,迫使优化器不跨屏障重新排序内存访问。
例如,如果您需要以特定顺序访问某个地址(可能是因为该内存区域实际上由不同的设备而不是内存支持),您需要能够告诉编译器,否则它可能只会优化您的步骤为了效率。
假设在这种情况下,您必须增加地址中的一个值,读取某些内容并增加相邻地址中的另一个值。
int c(int *d, int *e) {
int r;
d[0] += 1;
r = e[0];
d[1] += 1;
return r;
}
问题是编译器(gcc
在这种情况下)可以重新安排你的内存访问以获得更好的性能,如果你要求的话(-O
)。可能会导致如下所示的一系列指令:
00000000 <c>:
0: 4603 mov r3, r0
2: c805 ldmia r0, {r0, r2}
4: 3001 adds r0, #1
6: 3201 adds r2, #1
8: 6018 str r0, [r3, #0]
a: 6808 ldr r0, [r1, #0]
c: 605a str r2, [r3, #4]
e: 4770 bx lr
以上数值为d[0]
and d[1]
同时加载。假设这是您想要避免的事情,那么您需要告诉编译器不要重新排序内存访问,即使用asm volatile("" ::: "memory")
.
int c(int *d, int *e) {
int r;
d[0] += 1;
r = e[0];
asm volatile("" ::: "memory");
d[1] += 1;
return r;
}
所以你会得到你想要的指令序列:
00000000 <c>:
0: 6802 ldr r2, [r0, #0]
2: 4603 mov r3, r0
4: 3201 adds r2, #1
6: 6002 str r2, [r0, #0]
8: 6808 ldr r0, [r1, #0]
a: 685a ldr r2, [r3, #4]
c: 3201 adds r2, #1
e: 605a str r2, [r3, #4]
10: 4770 bx lr
12: bf00 nop
应该注意的是,这只是编译时内存屏障,以避免编译器重新排序内存访问,因为它没有放置额外的硬件级指令来刷新内存或等待加载或存储完成。如果 CPU 具有架构功能并且内存地址有效,它们仍然可以对内存访问进行重新排序normal
键入而不是strongly ordered
or device
(ref http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0211k/Babcddgd.html).