Para aquellos que no desean leer una publicación larga, esta optimización se llama (en LLVM) Loop Unswitch.
¿Por qué no preguntarle a un compilador?
void foo(char* c);
int main(int argc, char **argv) {
bool const condition = argc % 2;
for (int i = 0; i != argc; ++i) {
if (condition) {
foo(argv[1]);
} else {
foo(argv[0]);
}
}
return 0;
}
se transforma en el formulario SSA (a través de LLVM try out):
define i32 @main(i32 %argc, i8** nocapture %argv) {
entry:
%0 = icmp eq i32 %argc, 0 ; <i1> [#uses=1]
br i1 %0, label %bb5, label %bb.nph
bb.nph: ; preds = %entry
%1 = and i32 %argc, 1 ; <i32> [#uses=1]
%toBool = icmp eq i32 %1, 0 ; <i1> [#uses=1]
%2 = getelementptr inbounds i8** %argv, i64 1 ; <i8**> [#uses=1]
br i1 %toBool, label %bb3.us, label %bb3
bb3.us: ; preds = %bb3.us, %bb.nph
%i.07.us = phi i32 [ %4, %bb3.us ], [ 0, %bb.nph ] ; <i32> [#uses=1]
%3 = load i8** %argv, align 8 ; <i8*> [#uses=1]
tail call void @_Z3fooPc(i8* %3)
%4 = add nsw i32 %i.07.us, 1 ; <i32> [#uses=2]
%exitcond = icmp eq i32 %4, %argc ; <i1> [#uses=1]
br i1 %exitcond, label %bb5, label %bb3.us
bb3: ; preds = %bb3, %bb.nph
%i.07 = phi i32 [ %6, %bb3 ], [ 0, %bb.nph ] ; <i32> [#uses=1]
%5 = load i8** %2, align 8 ; <i8*> [#uses=1]
tail call void @_Z3fooPc(i8* %5)
%6 = add nsw i32 %i.07, 1 ; <i32> [#uses=2]
%exitcond8 = icmp eq i32 %6, %argc ; <i1> [#uses=1]
br i1 %exitcond8, label %bb5, label %bb3
bb5: ; preds = %bb3, %bb3.us, %entry
ret i32 0
}
No muy legible tal vez, por lo que quiero señalar lo que hay aquí:
entry
: comprobar si argc
es igual a 0, si es así, vaya a bb5
(salir) else vaya a bb.nph
bb.nph
: calcular el valor de condition
, si es verdad, ir a bb3.us
bien ir a bb3
bb3.us
y bb3
: bucles para la verdadera y falsa condición respectivamente
bb5
: salida
Un compilador puede bastante Transforme mucho su código como lo desee, siempre y cuando el efecto sea similar al que solicitó. En este caso, se ha vuelto a escribir de manera efectiva el código como:
int main(int argc, char**argv) {
if (argc != 0)
{
int i = 0;
if (argc % 2) {
do {
foo(argv[1]);
++i;
} while (i != argc);
} else {
do {
foo(argv[0]);
++i;
} while (i != argc);
}
}
return 0;
}
Es una forma de optimización de invariantes de bucle, combinada aquí con un primer control para evitar el cálculo de la condición si el bucle no se va a poner ejecutado.
Para aquellos de nosotros que pensarían que la primera solución es más clara, estamos muy contentos de que el compilador nos haga la optimización esencial para nosotros.
si 'condición' no está cambiando ¿por qué lo pondría dentro de 'para'? – nacho4d
@ nacho4d: Para evitar la duplicación de código (el encabezado 'for' y otras instrucciones fuera del' if' pero dentro del 'for') – erenon
¿Declaraste' condición' como 'volátil'? – mvds