为什么clang会忽略__restrict__?

gex*_*ide 6 c++ gcc clang restrict

我刚刚测试了一个小例子来检查__restrict__C++ 是否适用于最新的编译器:

void foo(int x,int* __restrict__ ptr1, int& v2) {
   for(int i=0;i<x;i++) {
       if(*ptr1==v2) {
           ++ptr1;
       } else {
           *ptr1=*ptr1+1;
       }
   }
}
Run Code Online (Sandbox Code Playgroud)

当使用最新的gcc(gcc8.1 -O3 -std = c ++ 14)在godbolt.org上尝试时,__restrict__按预期工作:v2只加载一次,因为它不能用别名ptr1.

以下是相关的装配部件:

.L5:
  mov eax, DWORD PTR [rsi]
  cmp eax, ecx # <-- ecx contains v2, no load from memory
  jne .L3
  add edx, 1
  add rsi, 4
  cmp edi, edx
  jne .L5
Run Code Online (Sandbox Code Playgroud)

现在与最新的clang相同(clang 6.0.0 -O3 -std = c ++ 14).它将循环展开一次,因此生成的代码要大得多,但这里是要点:

.LBB0_3: # =>This Inner Loop Header: Depth=1
  mov edi, dword ptr [rsi]
  cmp edi, dword ptr [rdx] # <-- restrict didn't work, v2 loaded from memory in hot loop
  jne .LBB0_9
  add rsi, 4
  mov edi, dword ptr [rsi]
  cmp edi, dword ptr [rdx] # <-- restrict didn't work, v2 loaded from memory in hot loop
  je .LBB0_12
Run Code Online (Sandbox Code Playgroud)

为什么会这样?我知道这__restrict__是非标准的,编译器可以自由地忽略它,但它似乎是一种非常基本的技术,可以从一些代码中获取最后一点性能,所以我怀疑clang在支持和支持时只是不支持它忽略关键字本身.那么,这里的问题是什么?我做错了吗?

yug*_*ugr 7

这么多无用的评论...

这似乎是 Clang 别名分析器中的一个错误。如果您将类型更改v2short编译器,则会根据基于类型的别名规则愉快地将其从循环中删除:

for.body:                                         ; preds = %for.inc, %for.body.lr.ph
  %i.09 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.inc ]
  %ptr1.addr.08 = phi i32* [ %ptr1, %for.body.lr.ph ], [ %ptr1.addr.1, %for.inc ]
  %1 = load i32, i32* %ptr1.addr.08, align 4, !tbaa !5
  %cmp1 = icmp eq i32 %1, %conv
  br i1 %cmp1, label %if.then, label %if.else
Run Code Online (Sandbox Code Playgroud)

但是使用原始循环,您会为两个内存引用设置相同的别名,这就是中端无法对其进行优化的原因:

  %i.08 = phi i32 [ %inc, %for.inc ], [ 0, %for.body.preheader ]
  %ptr1.addr.07 = phi i32* [ %ptr1.addr.1, %for.inc ], [ %ptr1, %for.body.preheader ]
  %0 = load i32, i32* %ptr1.addr.07, align 4, !tbaa !1
  %1 = load i32, i32* %v2, align 4, !tbaa !1
  %cmp1 = icmp eq i32 %0, %1
  br i1 %cmp1, label %if.then, label %if.else
Run Code Online (Sandbox Code Playgroud)

请注意!tbaa !1附加到两个内存引用,这意味着编译器无法区分它们中任何一个访问的内存。restrict一路上好像注释掉了……

我鼓励你用最新的 Clang 重现这个,并在LLVM Bugzilla 中提交一个错误(一定要抄送 Hal Finkel)。