编译器如何在ARM汇编中保障C++ volatile的存储顺序
你观察到的无内存屏障生成的现象,本质是混淆了volatile的语义边界、不同层级的重排序规则,以及ARM架构的内存属性约束,具体可以拆成三点说明:
编译器层面的顺序要求已经被满足
C++标准中对volatile访问的顺序约束,仅作用于编译阶段:它只要求编译器不得重排两个volatile访存操作的生成指令顺序,必须严格按照代码书写的先后顺序输出对应的load、store指令。你看到的汇编代码中,写x的str r1, [r3]明确排在写y的str r2, [r3, #4]之前,这就已经完全符合volatile对编译器的要求,标准从来没有强制要求编译器必须插入内存屏障指令。
ARM的存储重排序规则不是对所有内存生效
你提到的"ARM允许存储-存储重排序"的特性,仅针对标记为「普通内存(Normal Memory)」的地址区间生效——这类区间就是程序日常使用的栈、堆、全局变量存储区,CPU为了性能可以对其做乱序执行、缓存、预取等优化。
而volatile的设计初衷根本不是多线程同步,而是用于访问内存映射IO(MMIO)的外设寄存器。这类外设对应的物理地址,在系统初始化时会被页表标记为「设备内存(Device Memory)」类型,CPU对这类内存的访存有严格的顺序约束:对设备内存的访问会严格按照指令流的顺序执行,不会发生硬件层面的乱序,自然不需要额外插入dmb、dsb这类屏障指令。
普通全局变量场景下volatile本来就不提供硬件顺序保证
你测试用的x、y是普通全局变量,链接时被分配到了普通内存区域,属于Normal Memory范畴:
- 这种场景下volatile确实只能阻止编译器重排指令,没法阻止CPU硬件层面的存储重排,其他核心观测到的写入顺序可能和代码顺序不一致——但这不属于volatile的职责范围。
- 如果需要跨核心、跨线程的访存顺序保证,应该使用C++标准库的
std::atomic原子类型,根据需求指定对应的内存序,编译器会自动为目标架构生成必要的屏障指令,不要依赖volatile做线程同步。
注意:部分强内存模型架构(比如x86本身硬件就不允许存储-存储重排序),或者部分编译器的扩展实现(比如MSVC曾为volatile附加了acquire/release语义),会让volatile看起来自带硬件顺序保证,但这都不是C++标准规定的通用行为,跨平台开发时不能依赖这种特性。
内容的提问来源于stack exchange,提问作者Sourav Kannantha B

