c - 为什么 gcc 和 clang 没有将 strlen 提升到这个循环之外?

标签 c gcc clang compiler-optimization hoisting

考虑以下代码:

#include <string.h>

void bar(char c);

void foo(const char* restrict ss) {
    for (int i = 0; i < strlen(ss); ++i) {
        bar(*ss);
    }
}    

我希望 strlen(ss) 在这些基本理想的条件下被提升到循环之外;然而 - it isn't, neither by clang 5.0 nor by gcc 7.3最大优化 (-O3)。

为什么会这样?

注意:灵感来自(我的回答)this question .

最佳答案

其他答案声称无法提升 strlen 调用,因为字符串的内容可能会在调用之间发生变化。这些答案没有正确解释 restrict 的语义;即使 bar 可以通过全局变量或其他机制访问字符串,restrict 指针到 const 类型的语义也应该 (请参阅警告) 禁止 bar 修改字符串。

来自 C11, N1570 draft, 6.7.3.1 :

1 Let D be a declaration of an ordinary identifier that provides a means of designating an object P as a restrict-qualified pointer to type T.

2 If D appears inside a block and does not have storage class extern, let B denote the block. If D appears in the list of parameter declarations of a function definition, let B denote the associated block. Otherwise, let B denote the block of main (or the block of whatever function is called at program startup in a freestanding environment).

3 In what follows, a pointer expression E is said to be based on object P if (at some sequence point in the execution of B prior to the evaluation of E) modifying P to point to a copy of the array object into which it formerly pointed would change the value of E.137) Note that ''based'' is defined only for expressions with pointer types.

4 During each execution of B, let L be any lvalue that has &L based on P. If L is used to access the value of the object X that it designates, and X is also modified (by any means), then the following requirements apply: T shall not be const-qualified. Every other lvalue used to access the value of X shall also have its address based on P. Every access that modifies X shall be considered also to modify P, for the purposes of this subclause. If P is assigned the value of a pointer expression E that is based on another restricted pointer object P2, associated with block B2, then either the execution of B2 shall begin before the execution of B, or the execution of B2 shall end prior to the assignment. If these requirements are not met, then the behavior is undefined.

5 Here an execution of B means that portion of the execution of the program that would correspond to the lifetime of an object with scalar type and automatic storage duration associated with B.

这里,声明Dconst char* __restrict__ ss,关联的 block Bfoo<的主体strlen 访问字符串的所有左值都具有基于 ss(见警告)&L,并且这些访问发生在 B 执行期间(因为根据第 5 节中的定义,strlen 的执行是 B 执行的一部分)。 ss 指向一个 const 限定的类型,因此在第 4 节中,允许编译器假设 strlen 访问的字符串元素在 foo 的执行期间没有被修改;修改它们将是未定义的行为。

(警告)上述分析假定 strlen 通过“普通”指针解引用或索引访问字符串。如果 strlen 使用 SSE 内在函数或内联汇编等技术,我不清楚此类访问在技术上是否算作使用左值来访问它指定的对象的值。如果它们不算在内,restrict 的保护可能不适用,并且编译器可能无法执行提升。

也许上述警告会使 restrict 的保护无效。也许编译器对 strlen 的定义了解不够,无法分析它与 restrict 的交互(我很惊讶它没有内联)。也许编译器可以自由执行提升,只是没有意识到;也许一些相关的优化没有实现,或者它未能在正确的编译器组件之间传播必要的信息。确定确切原因需要比我更熟悉 GCC 和 Clang 内部结构。

Further-simplified tests消除了 strlen 并且循环显示 Clang 确实支持 restrict-pointer-to-const 优化,但我无法从 GCC 观察到任何此类支持。

关于c - 为什么 gcc 和 clang 没有将 strlen 提升到这个循环之外?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48482003/

相关文章:

c++ - 我可以在 MacOS X 10.11 上让 gcc/g++ 指向 ACTUAL gcc/g++ 吗?

c - 在哪里更改 LD_LIBRARY_PATH

objective-c - 如何强制发生 “Expression result unused” 警告

c - 内存指针运算时如何避免valgrind report mem lost?

c - Makefile:通配符和 patsubst 不会更改文件源名称

GCC 内部结构 : Where are fake dereferences removed?

clang - 如何为 Clang 添加 AVR 支持?

c++ - 如何使用 clang++ 作为编译器链接我的控制台应用程序以 boost OSX 环境中/usr/local/lib 中存在的库

c - 没有静态存储的移动平均线故障

c - 枚举值的字符串表示