char* 与先前指令中设置的值的比较未优化?

char* comparison against value set in previous instruction not optimized away?

在摆弄一些测试代码时,我在以下代码中观察到:

extern char* pc;
int muysimple() {
  *pc = 0;
  if (*pc != 0) { return 1111; }

  return 4444;
}

比较 未被 任何编译器优化,我可以 select 在 godbolt.

如果我将 extern char* 更改为 extern int*,比较 将被优化掉

为什么 gcc 和 clang 都保留 char 的比较,即使这段代码中似乎没有任何内容可以合法地更改值,以便比较的计算结果为真?

语言规范中是否有任何内容禁止 write/read 到 char* 的这种优化(但 允许 int* 的优化!)或者优化器只是围绕 char 更保守?


正如评论中已经提到的那样:我不明白——明智的规则——别名规则如何在这里发挥作用。此代码只有一个变量可见,没有多线程或调用任何地方。

当然,char 别名规则很可能是编译器不优化它的实际原因,但这里的问题是编译器 是否会 可以针对 char* 进行优化——就像他们针对 int* 所做的那样——或者他们实际上是否不允许对此进行优化。

也许 char* 被允许使用别名 任何东西 包括 volatile 内存。

使用 -fpermissive 标志,gcc 只是给出有关 char* 指向易失性内存的警告,但是 compiles.

由于在您的示例中指针是 extern,因此可能使用此标志编译了另一个翻译,因此指针指向 volatile 内存。因此无法进行优化。

我没有找到关于 non-volatile char* 别名 volatile 内存是否被视为合法或 UB 的官方参考。

编辑: 看起来将 int* 分配给 volatile int* 会给出相同的警告,在这种情况下,编译器会进行优化。所以我猜 warning/permissive 标志不相关。问题只是通常是否允许 char*volatile 内存进行别名。

这是 "cleverly" 的 example 别名 volatile struct 仅使用 C++ 强制转换,没有警告。

EDIT2:看起来添加 -fno-strict-aliasing 标志会导致 any 指针类型无法在原始代码中进行优化。因此,我猜测编译器在警惕别名时不进行优化的原因。要么是因为:

  1. 允许使用别名 volatile 内存 (?)
  2. 担心数据会与指向同一 non-volatile 内存的其他指针争用。 @alain 在他的回答中提到数据竞争是 UB,但即使这是真的,我猜编译器制造商决定在这里要小心,而不是优化。

even though there seems to be nothing in this code that could legally change the value so that the comparison ever evaluates to true?

pc = (char *) &pc; 是有效的赋值,并且由于 char 左值可用于访问任何 POD 类型,因此 *pc 的赋值可能会更改 pc.