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 指针类型无法在原始代码中进行优化。因此,我猜测编译器在警惕别名时不进行优化的原因。要么是因为:
- 允许使用别名
volatile
内存 (?)
- 担心数据会与指向同一 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
.
在摆弄一些测试代码时,我在以下代码中观察到:
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 指针类型无法在原始代码中进行优化。因此,我猜测编译器在警惕别名时不进行优化的原因。要么是因为:
- 允许使用别名
volatile
内存 (?) - 担心数据会与指向同一 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
.