为什么Clang优化这个代码?

代码的目的是find代表0到1之间的值的32位浮点位模式的总数。在我看来,这应该工作,但由于某种原因,从铿的组装输出基本上是return 0;

我用Clang 3.3和Clang 3.4.1编译,使用-std=c++1y -Wall -Wextra -pedantic -O2-std=c++1y -Wall -Wextra -pedantic -O3

铿锵3.4使用-O2和-O3优化了所有的东西。

铿锵3.3只会优化-O3的所有function。

通过“优化一切”我的意思是这是程序的汇编输出:

 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; } 

这是一个更简单的testing用例,它指出这是一个编译器错误:

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.它不认为它是一个无限循环,在这种情况下,程序集不会从main返回。

这不是一个答案,而是一个太大而不能评论的数据点。

有趣的是,如果您在返回之前打印count ,那么clang 仍然会优化所有内容,并使用-O0-O31065353218打印0 。 (请注意, echo $?报告返回值总是 2,不pipe实际返回是什么)。 对我来说,这使得它看起来像一个编译器错误。

如果你把你的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++; } 

然后同样的答案出现在两个优化级别。 如果你打印的话,肯定是真的,尽pipe我没有看过assembly,但对于未打印的情况也是如此,因为在完成之前确实需要时间。 (叮当3.4)

我之前在LLVM中发现了一些bug(使clang出现段错误的有趣模板业务),如果您给出了一个清晰的故障示例,那么他们一直在响应修复它。 我build议你提交这个错误报告。

使用mukunda上面的例子,在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 } 

优化器稍后擦除无法访问和无效的代码 – 这几乎是整个函数体。