GCC/Clang 是否应该通过一组限制条件指针来优化这种冗余负载?

Tie*_*ies 5 c gcc clang restrict-qualifier

我正在研究通过将 C99限制类型限定符添加到数组的类型参数来允许编译器进行的优化。

例如,考虑下面的函数f

int f(int* restrict a[2]) {
    *(a[0]) = 10;
    *(a[1]) = 11;
    return *(a[0]);
}
Run Code Online (Sandbox Code Playgroud)

它采用一个由指向整数的限制指针组成的二元素数组a作为参数。该函数存储10到对象a[0]指向和11对象a[1]指向。a[0]最后,它返回该对象指向的值。

因为指针a[0]a[1]是限制限定的,所以我假设编译器有足够的信息来执行典型的优化,即不再次加载a[0],而是直接返回(因为如果程序已定义行为,则10写入a[1]不能更改对象指向a[0])。

因此,如果优化发生在“C 代码级别”,则该函数将变为:

int f(int* restrict a[2]) {
    *(a[0]) = 10;
    *(a[1]) = 11;
    return 10;
}
Run Code Online (Sandbox Code Playgroud)

然而,GCC 和 Clang(我尝试了几个版本,其中最新的是 Godbolt)都没有执行此优化(当使用 -O3 编译时)。

GCC13.2 发出的 X86 程序集是

f:
        mov     rax, QWORD PTR [rdi]
        mov     rdx, QWORD PTR [rdi+8]
        mov     DWORD PTR [rax], 10
        mov     DWORD PTR [rdx], 11
        mov     eax, DWORD PTR [rax]
        ret
Run Code Online (Sandbox Code Playgroud)

正如我们所看到的,执行了从寄存器的重新加载,[rax]而不是仅仅10移入eax

一个类似的程序,其中指针作为单独的参数而不是指针数组传递,确实展示了两个编译器的预期优化:

int g(int* restrict p, int* restrict q) {
    *p = 10;
    *q = 11;
    return *p;
}
Run Code Online (Sandbox Code Playgroud)

被编译成:

g:
        mov     DWORD PTR [rdi], 10
        mov     eax, 10
        mov     DWORD PTR [rsi], 11
        ret
Run Code Online (Sandbox Code Playgroud)

我是否误解了限制限定指针作为数组的类型参数的语义,或者预期的优化对于上述编译器来说太难了?

gul*_*lpr 0

无法完成此优化,并且您的第二个示例无效(因为忘记了一级间接)。

  1. 即使您在将其作为数组访问时限制两个指针,如果通过第二个元素的访问不会修改第一个元素,您也不会违反与编译器的约定。所以必须重新加载。
int f(int* restrict * restrict a) {
    *(a[0]) = 10;
    *(a[1]) = 11;
    return *(a[0]);
}

//this function is to illustrate the issue
void foo(void)
{
    int a;
    int *b[2] = {&a,&a};
    f(b);
}
Run Code Online (Sandbox Code Playgroud)
  1. 您的指针示例需要使用双指针而不是单指针。
int g(int* restrict * restrict p, int* restrict * restrict q) {
    **p = 10;
    **q = 11;
    return **p;
}
Run Code Online (Sandbox Code Playgroud)

这将生成优化的代码,因为您无法访问使用引用的对象*p而不*q违反您向编译器做出的承诺。

        mov     rax, QWORD PTR [rdi]
        mov     DWORD PTR [rax], 10
        mov     rax, QWORD PTR [rsi]
        mov     DWORD PTR [rax], 11
        mov     eax, 10
        ret
Run Code Online (Sandbox Code Playgroud)

  • 没有明显的理由说明必须需要指针到指针。是的,在OP的情况下,指针本身可以在访问之间修改,当然。这也不能解决问题 https://godbolt.org/z/3bKEdr7GK 并且没有明显的原因。 (2认同)