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] 和 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 或device (ref),CPU 仍然可以重新排序内存访问。