编译器是否需要将存储发送到原始地址?

Cha*_*son 1 c c++ compiler-optimization language-lawyer

两种流行的编译器(gcc、clang)在以下函数体中发出存储指令:

void foo(char x) {
  *(char *)0xE0000000 = x;
}
Run Code Online (Sandbox Code Playgroud)

该程序可能在某些硬件架构上正常运行,其中写入的地址是内存映射 IO。

由于此访问是通过未限定为的指针执行的volatile,因此编译器是否需要在此处发出存储?足够积极的优化器可以合法地消除这个存储吗?我很好奇这个存储是否对抽象机构成了可观察到的副作用。

另外,C17 和 C++20 在这方面有什么不同吗?

Eri*_*hil 5

C 标准不需要实现来发布存储,因为 C 2018 5.1.2.3 6 说:

\n
\n

一致实施的最低要求是:

\n

\xe2\x80\x94 对易失性对象的访问严格按照抽象机的规则进行评估。

\n

\xe2\x80\x94 在程序终止时,写入文件的所有数据应与根据抽象语义执行程序所产生的结果相同。

\n

\xe2\x80\x94 交互设备的输入和输出动态应按照 7.21.3 的规定进行。这些要求的目的是尽快出现无缓冲或行缓冲的输出,以确保提示消息实际上在程序等待输入之前出现。

\n

这是程序的可观察行为。

\n
\n

这些都不包括对非易失性左值的赋值。

\n

  • 请注意,[C++ 使用基本相同的规则](https://timsong-cpp.github.io/cppwp/n4861/intro.abstract#6)。 (3认同)
  • 还值得注意的是,即使使用“易失性”仅阻止相对于其他“易失性”存储/读取的编译器级重新排序,它也不会阻止指令或管道级重新排序。这可能需要使用内在函数。 (2认同)
  • 不是直接的,@DavidSchwartz,但是 C 规范*确实*说它根据抽象机定义了语言语义。易失性访问是一种副作用,并且该副作用不能相对于抽象机上的其他副作用或由抽象机执行的评估进行重新排序,但是该语言对于非抽象机建模的易失性访问的任何影响没有任何说明机器。 (2认同)
  • @DavidSchwartz 你将标准与 CPU 架构混淆了。我正在描述一种导致 Linux 内核出现一些重大问题的情况(并且涉及到使用“cpuid”指令来解决所有问题)。该标准具有 AS-IF,“易失性”仅受此限制。但它不会锁定或阻止管道级别重新排序。因此,如果写入设备的寄存器 A **必须**发生在写入寄存器 B 之前,那么您需要一些东西来强制管道序列化。这不是标准的一部分。这是 RISC-V 和 AlphaAXP 上的一个主要问题。 (2认同)