Почему лязг игнорирует __restrict__? - PullRequest
0 голосов
/ 16 мая 2018

Я только что протестировал небольшой пример, чтобы проверить, работает ли __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;
       }
   }
}

При попытке выполнить это на godbolt.org с последней версией gcc (gcc8.1 -O3 -std = c ++ 14), __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

Теперь то же самое с последним лязгом (лязг 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

Почему это так? Я знаю, что __restrict__ не является стандартным, и компилятор может его игнорировать, но это, кажется, очень фундаментальный метод для получения последней части производительности из своего кода, поэтому я сомневаюсь, что clang просто не поддерживает его поддерживая и игнорируя само ключевое слово. Итак, в чем здесь проблема? Я что-то не так делаю?

1 Ответ

0 голосов
/ 29 октября 2018

Так много бесполезных комментариев ...

Кажется, это ошибка в анализаторе псевдонимов Clang.Если вы измените тип с v2 на short, компилятор удачно удалит его из цикла на основе правил псевдонимов на основе типов:

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

Но с оригинальным циклом вы получите одинаковый набор псевдонимов для обеих ссылок на памятьВот почему средний класс не может его оптимизировать:

  %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

Обратите внимание на !tbaa !1, прикрепленный к обоим ссылкам на память, что означает, что компилятор не может различить память, к которой обращается один из них.Похоже, что restrict аннотация была потеряна по пути ...

Я рекомендую вам воспроизвести это с последним Clang и сообщить об ошибке в LLVM Bugzilla (обязательно cc HalФинкель).

...