c++ clang compiler-optimization

c++ - ¿Por qué Clang está optimizando este código?



compiler-optimization (3)

El propósito del código es encontrar el número total de patrones de bits de punto flotante de 32 bits que representan valores entre 0 y 1. Me parece que esto debería funcionar, pero por alguna razón, la salida del ensamblaje de Clang es básicamente el equivalente de return 0; .

-std=c++1y -Wall -Wextra -pedantic -O2 esto con Clang 3.3 y Clang 3.4.1, usando -std=c++1y -Wall -Wextra -pedantic -O2 y -std=c++1y -Wall -Wextra -pedantic -O3

Clang 3.4 optimiza todo lejos con -O2 y -O3.

Clang 3.3 solo optimiza todo lejos con -O3.

Por "optimiza todo lo que está lejos" quiero decir que esta es la salida de ensamblaje del programa:

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


Aquí hay un caso de prueba más simple que señala que es un error del compilador:

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 }

El ensamblaje muestra que genera 1, en lugar de 6. Tampoco cree que sea un bucle infinito, en cuyo caso el ensamblaje no regresa de main.


Esto no es una respuesta, sino un punto de datos que es demasiado grande para un comentario.

Curiosamente, si imprime el count justo antes de la devolución, entonces Clang seguirá optimizando todo e imprimirá 0 con 1065353218 y 1065353218 con -O0 . (Tenga en cuenta que echo $? Informa que el valor de retorno siempre es 2, sin importar cuál fue el retorno real). Para mí, esto hace que parezca un error de compilación.

Si te conviertes en un 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++; }

Entonces sale la misma respuesta para ambos niveles de optimización. Definitivamente es cierto si imprime, y aunque no he visto el ensamblaje, es probable que también lo sea en el caso no impreso porque toma tiempo antes de que termine. (Clang 3.4)

He encontrado errores en LLVM antes (negocio de plantillas divertidas que hicieron clang segfault) y han respondido para solucionarlo si se da un ejemplo claro de la falla. Le sugiero que envíe esto como un informe de error.


Usando el ejemplo anterior de mukunda, en el ángulo 3.4 con -O2, el problema parece estar en la fase de vectorización. El código vectorizado salta en la entrada para pasar el código vectorizado:

br i1 true, label %middle.block, label %vector.ph

por lo que el valor del count permanece sin cambios desde su inicialización.

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

Posteriormente, el optimizador borra el código inalcanzable y no efectivo, que es casi la totalidad del cuerpo de la función.