Почему Clang оптимизирует этот код?
Цель кода - найти общее количество 32-битных битовых шаблонов с плавающей запятой, которые представляют значения от 0 до 1. Мне кажется, что это должно сработать, но почему-то сборка, выводимая из Clang, в основном эквивалент return 0;
.
Я скомпилировал это с помощью Clang 3.3 и Clang 3.4.1, используя -std=c++1y -Wall -Wextra -pedantic -O2
и -std=c++1y -Wall -Wextra -pedantic -O3
Clang 3.4 оптимизирует все с помощью -O2 и -O3.
Clang 3.3 только оптимизирует все с помощью -O3.
Под "оптимизирует все" я подразумеваю, что это сборка выхода программы:
main: # @main
xorl %eax, %eax
ret
#include <limits>
#include <cstring>
#include <cstdint>
template <class TO, class FROM>
inline TO punning_cast(const FROM &input)
{
TO out;
std::memcpy(&out, &input, sizeof(TO));
return out;
}
int main()
{
uint32_t i = std::numeric_limits<uint32_t>::min();
uint32_t count = 0;
while (1)
{
float n = punning_cast<float>(i);
if(n >= 0.0f && n <= 1.0f)
count++;
if (i == std::numeric_limits<uint32_t>::max())
break;
i++;
}
return count;
}
Ответы
Ответ 1
Здесь приведен более простой тестовый пример, который указывает, что это ошибка компилятора:
http://coliru.stacked-crooked.com/a/58b3f9b4edd8e373
#include <cstdint>
int main()
{
uint32_t i = 0;
uint32_t count = 1;
while (1)
{
if( i < 5 )
count+=1;
if (i == 0xFFFFFFFF)
break;
i++;
}
return count; // should return 6
}
Ассемблер показывает, что он выводит 1, а не 6. Он не считает его бесконечным циклом, и в этом случае сборка не возвращается из основного.
Ответ 2
Это не ответ, а дататайт, слишком большой для комментария.
Интересно, что если вы напечатаете count
прямо перед возвратом, то clang все равно оптимизирует все и распечатает 0
с помощью -O3
и 1065353218
с помощью -O0
. (Обратите внимание, что echo $?
сообщает, что возвращаемое значение всегда равно 2, независимо от фактического возврата). Для меня это делает его похожим на ошибку компилятора.
Если вы превратите свой while
в for
:
for (uint32_t i = std::numeric_limits<uint32_t>::min(); i != std::numeric_limits<uint32_t>::max(); ++i)
{
float n = punning_cast<float>(i);
if(n >= 0.0f && n <= 1.0f)
count++;
}
Затем тот же ответ появляется для обоих уровней оптимизации. Определенно верно, если вы печатаете, и хотя я не смотрел на сборку, это, вероятно, также верно для непечатаемого футляра, потому что для его завершения требуется время. (clang 3.4)
Я обнаружил ошибки в LLVM раньше (забавный бизнес шаблонов, который сделал clang segfault), и они были восприимчивы к его исправлению, если вы дадите хороший и ясный пример ошибки. Я предлагаю вам представить это как отчет об ошибке.
Ответ 3
Используя пример mukunda выше, в clang 3.4 с -O2 проблема, кажется, находится в фазе векторизации. Вектор векторизованного кода перескакивает при входе в векторизованный код:
br i1 true, label %middle.block, label %vector.ph
поэтому значение count
не изменяется от его инициализации.
*** IR Dump Before Combine redundant instructions ***
; Function Attrs: nounwind readnone ssp uwtable
define i32 @main() #0 {
entry:
br i1 true, label %middle.block, label %vector.ph
vector.ph: ; preds = %entry
br label %vector.body
vector.body: ; preds = %vector.body, %vector.ph
%index = phi i32 [ 0, %vector.ph ], [ %index.next, %vector.body ]
%vec.phi = phi <4 x i32> [ <i32 1, i32 0, i32 0, i32 0>, %vector.ph ], [ %4, %vector.body ]
%vec.phi8 = phi <4 x i32> [ zeroinitializer, %vector.ph ], [ %5, %vector.body ]
%broadcast.splatinsert = insertelement <4 x i32> undef, i32 %index, i32 0
%broadcast.splat = shufflevector <4 x i32> %broadcast.splatinsert, <4 x i32> undef, <4 x i32> zeroinitializer
%induction = add <4 x i32> %broadcast.splat, <i32 0, i32 1, i32 2, i32 3>
%induction7 = add <4 x i32> %broadcast.splat, <i32 4, i32 5, i32 6, i32 7>
%0 = icmp ult <4 x i32> %induction, <i32 5, i32 5, i32 5, i32 5>
%1 = icmp ult <4 x i32> %induction7, <i32 5, i32 5, i32 5, i32 5>
%2 = zext <4 x i1> %0 to <4 x i32>
%3 = zext <4 x i1> %1 to <4 x i32>
%4 = add <4 x i32> %2, %vec.phi
%5 = add <4 x i32> %3, %vec.phi8
%6 = icmp eq <4 x i32> %induction, <i32 -1, i32 -1, i32 -1, i32 -1>
%7 = icmp eq <4 x i32> %induction7, <i32 -1, i32 -1, i32 -1, i32 -1>
%8 = add <4 x i32> %induction, <i32 1, i32 1, i32 1, i32 1>
%9 = add <4 x i32> %induction7, <i32 1, i32 1, i32 1, i32 1>
%index.next = add i32 %index, 8
%10 = icmp eq i32 %index.next, 0
br i1 %10, label %middle.block, label %vector.body, !llvm.loop !1
middle.block: ; preds = %vector.body, %entry
%resume.val = phi i32 [ 0, %entry ], [ 0, %vector.body ]
%trunc.resume.val = phi i32 [ 0, %entry ], [ 0, %vector.body ]
%rdx.vec.exit.phi = phi <4 x i32> [ <i32 1, i32 0, i32 0, i32 0>, %entry ], [ %4, %vector.body ]
%rdx.vec.exit.phi9 = phi <4 x i32> [ zeroinitializer, %entry ], [ %5, %vector.body ]
%bin.rdx = add <4 x i32> %rdx.vec.exit.phi9, %rdx.vec.exit.phi
%rdx.shuf = shufflevector <4 x i32> %bin.rdx, <4 x i32> undef, <4 x i32> <i32 2, i32 3, i32 undef, i32 undef>
%bin.rdx10 = add <4 x i32> %bin.rdx, %rdx.shuf
%rdx.shuf11 = shufflevector <4 x i32> %bin.rdx10, <4 x i32> undef, <4 x i32> <i32 1, i32 undef, i32 undef, i32 undef>
%bin.rdx12 = add <4 x i32> %bin.rdx10, %rdx.shuf11
%11 = extractelement <4 x i32> %bin.rdx12, i32 0
%cmp.n = icmp eq i32 0, %resume.val
br i1 %cmp.n, label %while.end, label %scalar.ph
scalar.ph: ; preds = %middle.block
br label %while.body
while.body: ; preds = %while.body, %scalar.ph
%i.0 = phi i32 [ %trunc.resume.val, %scalar.ph ], [ %inc, %while.body ]
%count.0 = phi i32 [ %11, %scalar.ph ], [ %add.count.0, %while.body ]
%cmp = icmp ult i32 %i.0, 5
%add = zext i1 %cmp to i32
%add.count.0 = add i32 %add, %count.0
%cmp1 = icmp eq i32 %i.0, -1
%inc = add i32 %i.0, 1
br i1 %cmp1, label %while.end, label %while.body, !llvm.loop !4
while.end: ; preds = %middle.block, %while.body
%add.count.0.lcssa = phi i32 [ %add.count.0, %while.body ], [ %11, %middle.block ]
ret i32 %add.count.0.lcssa
}
Оптимизатор позже стирает недостижимый и неэффективный код, который является почти всем телом функции.