Fixture 125

loop shapes

C · 4 functions · 4 lanes · 16 of 16 function-lanes behave identically

All 4 lanes recompile and return the same results as the original.

while, do/while and for with the same intent lower differently: a do/while body always runs once, and a for loop with an empty condition needs an internal exit. Loop rotation at -O2 makes several of these converge.

tests/decompiler_fixtures/src/125_loop_shapes.c source
#include <stdint.h>

/* while, do/while and for with the same intent lower differently: a do/while
 * body always runs once, and a for loop with an empty condition needs an
 * internal exit. Loop rotation at -O2 makes several of these converge. */

__attribute__((noinline)) int32_t while_zero_trips(int32_t limit) {
    int32_t index = 0;
    int32_t total = 0;
    if (limit < 0 || limit > 16) {
        return -1;
    }
    while (index < limit) {
        total += index;
        index += 1;
    }
    return total * 10 + index;
}

__attribute__((noinline)) int32_t do_while_always_once(int32_t limit) {
    int32_t index = 0;
    int32_t total = 0;
    if (limit < 0 || limit > 16) {
        return -1;
    }
    do {
        total += index;
        index += 1;
    } while (index < limit);
    return total * 10 + index; /* differs from the while form at limit 0 */
}

__attribute__((noinline)) int32_t infinite_with_internal_exit(int32_t limit) {
    int32_t index = 0;
    int32_t total = 0;
    if (limit < 0 || limit > 16) {
        return -1;
    }
    for (;;) {
        if (index >= limit) {
            break;
        }
        total += index;
        index += 1;
    }
    return total * 10 + index;
}

__attribute__((noinline)) int32_t decrementing_loop(int32_t limit) {
    int32_t index;
    int32_t total = 0;
    if (limit < 0 || limit > 16) {
        return -1;
    }
    /* Counting down to zero lets the compiler test the flags from the
     * decrement itself instead of a separate comparison. */
    for (index = limit; index > 0; --index) {
        total += index;
    }
    return total * 10 + index;
}

Recovered C

Generated by glaurung decompile --style decbench at b47f6b43. baseline.json records the result after recompiling the C and calling it beside the original with seeded inputs.

clang -O0

4/4
decrementing_loop pass 26 lines
// glaurung: decrementing_loop @ 0x1260
int32_t decrementing_loop(int32_t arg0) {
    int total;
    int index;
    int local_4;
    // x86-64 prologue: save rbp
    total = 0;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    index = arg0;
    while (((((unsigned long)((unsigned int)(index)) == 0) | ((long)(index) < 0)) == 0)) {
        total = ((unsigned int)(index) + total);
        index = ((unsigned int)(index) - 1);
    }
    local_4 = ((total * 10) + index);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
do_while_always_once pass 26 lines
// glaurung: do_while_always_once @ 0x1170
int32_t do_while_always_once(int32_t arg0) {
    int index;
    int total;
    int local_4;
    // x86-64 prologue: save rbp
    index = 0;
    total = 0;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    do {
        total = ((unsigned int)(index) + total);
        index = ((unsigned int)(index) + 1);
    } while ((index < arg0));
    local_4 = ((total * 10) + index);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
infinite_with_internal_exit pass 26 lines
// glaurung: infinite_with_internal_exit @ 0x11e0
int32_t infinite_with_internal_exit(int32_t arg0) {
    int index;
    int total;
    int local_4;
    // x86-64 prologue: save rbp
    index = 0;
    total = 0;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    while ((index < arg0)) {
        total = ((unsigned int)(index) + total);
        index = ((unsigned int)(index) + 1);
    }
    local_4 = ((total * 10) + index);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
while_zero_trips pass 26 lines
// glaurung: while_zero_trips @ 0x1100
int32_t while_zero_trips(int32_t arg0) {
    int index;
    int total;
    int local_4;
    // x86-64 prologue: save rbp
    index = 0;
    total = 0;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    while ((index < arg0)) {
        total = ((unsigned int)(index) + total);
        index = ((unsigned int)(index) + 1);
    }
    local_4 = ((total * 10) + index);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}

clang -O2

4/4
decrementing_loop pass 23 lines
// glaurung: decrementing_loop @ 0x1190
int32_t decrementing_loop(int32_t arg0) {
    int total;
    long ret;
    int var2;
    long var20;
    int var21;
    long var5;
    long var6;
    ret = 0xffffffff;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return ret;
    }
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return 0;
    }
    var2 = ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) ? arg0 : 1);
    var5 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - var2)));
    var6 = (unsigned long)((unsigned int)((arg0 - 1)));
    var20 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var5)) * var6))) + arg0))) - (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((var6 - var2))) * var5))) >> 1))))));
    var21 = (var20 + var20);
    return (unsigned int)(((unsigned long)((unsigned int)(var21)) + ((unsigned long)((unsigned int)(var21)) * 4)));
}
do_while_always_once pass 15 lines
// glaurung: do_while_always_once @ 0x1130
int32_t do_while_always_once(int32_t arg0) {
    int index;
    int total;
    long ret;
    int var1;
    int var6;
    ret = 0xffffffff;
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(16))) {
        var1 = ((2 <= (long)(arg0)) ? arg0 : 1);
        var6 = (((unsigned long)(((unsigned long)((unsigned int)((var1 - 2))) * (unsigned long)((unsigned int)((var1 - 1))))) >> 1) + var1);
        ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((var1 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var6)) + ((unsigned long)((unsigned int)(var6)) * 4)))) * 2)))) - 10)));
    }
    return ret;
}
infinite_with_internal_exit pass 16 lines
// glaurung: infinite_with_internal_exit @ 0x1160
int32_t infinite_with_internal_exit(int32_t arg0) {
    int index;
    int total;
    long ret;
    int var5;
    ret = 0xffffffff;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return ret;
    }
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return 0;
    }
    var5 = (((unsigned long)(((unsigned long)((unsigned int)((arg0 - 2))) * (unsigned long)((unsigned int)((arg0 - 1))))) >> 1) + arg0);
    return (unsigned int)(((unsigned long)((unsigned int)((arg0 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var5)) + ((unsigned long)((unsigned int)(var5)) * 4)))) * 2)))) - 10));
}
while_zero_trips pass 16 lines
// glaurung: while_zero_trips @ 0x1100
int32_t while_zero_trips(int32_t arg0) {
    int index;
    int total;
    long ret;
    int var5;
    ret = 0xffffffff;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return ret;
    }
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return 0;
    }
    var5 = (((unsigned long)(((unsigned long)((unsigned int)((arg0 - 2))) * (unsigned long)((unsigned int)((arg0 - 1))))) >> 1) + arg0);
    return (unsigned int)(((unsigned long)((unsigned int)((arg0 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var5)) + ((unsigned long)((unsigned int)(var5)) * 4)))) * 2)))) - 10));
}

gcc -O0

4/4
decrementing_loop pass 24 lines
// glaurung: decrementing_loop @ 0x11f5
int32_t decrementing_loop(int32_t arg0) {
    int total;
    int index;
    int var6;
    // x86-64 prologue: save rbp
    total = 0;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    index = arg0;
    while (((((unsigned long)((unsigned int)(index)) == 0) | ((long)(index) < 0)) == 0)) {
        total = (total + (unsigned int)(index));
        index = (index - 1);
    }
    var6 = ((unsigned int)(((unsigned long)((unsigned int)(total)) << 2)) + (unsigned int)(total));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(index)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var6)) + (unsigned long)((unsigned int)(var6)))))));
}
do_while_always_once pass 24 lines
// glaurung: do_while_always_once @ 0x114c
int32_t do_while_always_once(int32_t arg0) {
    int index;
    int total;
    int var6;
    // x86-64 prologue: save rbp
    index = 0;
    total = 0;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    do {
        total = (total + (unsigned int)(index));
        index = (index + 1);
    } while ((index < arg0));
    var6 = ((unsigned int)(((unsigned long)((unsigned int)(total)) << 2)) + (unsigned int)(total));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(index)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var6)) + (unsigned long)((unsigned int)(var6)))))));
}
infinite_with_internal_exit pass 24 lines
// glaurung: infinite_with_internal_exit @ 0x119f
int32_t infinite_with_internal_exit(int32_t arg0) {
    int index;
    int total;
    int var6;
    // x86-64 prologue: save rbp
    index = 0;
    total = 0;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    while ((index < arg0)) {
        total = (total + (unsigned int)(index));
        index = (index + 1);
    }
    var6 = ((unsigned int)(((unsigned long)((unsigned int)(total)) << 2)) + (unsigned int)(total));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(index)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var6)) + (unsigned long)((unsigned int)(var6)))))));
}
while_zero_trips pass 24 lines
// glaurung: while_zero_trips @ 0x10f9
int32_t while_zero_trips(int32_t arg0) {
    int index;
    int total;
    int var6;
    // x86-64 prologue: save rbp
    index = 0;
    total = 0;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    while ((index < arg0)) {
        total = (total + (unsigned int)(index));
        index = (index + 1);
    }
    var6 = ((unsigned int)(((unsigned long)((unsigned int)(total)) << 2)) + (unsigned int)(total));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(index)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var6)) + (unsigned long)((unsigned int)(var6)))))));
}

gcc -O2

4/4
decrementing_loop pass 26 lines
// glaurung: decrementing_loop @ 0x11b0
int32_t decrementing_loop(int32_t arg0) {
    int index;
    int total;
    long ret;
    long var1;
    long var2;
    int var4;
    long var5;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    ret = 0;
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return ret;
    }
    var1 = 0;
    var2 = (unsigned long)((unsigned int)(arg0));
    do {
        var1 = (unsigned long)((unsigned int)((var1 + var2)));
        var4 = (var2 - 1);
        var2 = (unsigned long)((unsigned int)(var4));
    } while (((unsigned long)((unsigned int)(var4)) != 0));
    var5 = (unsigned long)((unsigned int)((var1 + (var1 * 4))));
    return (unsigned int)((var5 + var5));
}
do_while_always_once pass 18 lines
// glaurung: do_while_always_once @ 0x1140
int32_t do_while_always_once(int32_t arg0) {
    int index;
    int total;
    long var5;
    int var7;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    index = 0;
    var5 = 0;
    do {
        var5 = (unsigned long)((unsigned int)((var5 + index)));
        var7 = (index + 1);
        index = (unsigned long)((unsigned int)(var7));
    } while (((((unsigned int)(arg0) == (unsigned int)(var7)) | (arg0 < var7)) == 0));
    return (unsigned int)((((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) ? 1 : arg0) + ((unsigned long)((unsigned int)((var5 + (var5 * 4)))) * 2)));
}
infinite_with_internal_exit pass 21 lines
// glaurung: infinite_with_internal_exit @ 0x1170
int32_t infinite_with_internal_exit(int32_t arg0) {
    int index;
    int total;
    long var6;
    int var8;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return 0;
    }
    index = 0;
    var6 = 0;
    do {
        var6 = (unsigned long)((unsigned int)((var6 + index)));
        var8 = (index + 1);
        index = (unsigned long)((unsigned int)(var8));
    } while (((unsigned int)(arg0) != (unsigned int)(var8)));
    return (unsigned int)((arg0 + ((unsigned long)((unsigned int)((var6 + (var6 * 4)))) * 2)));
}
while_zero_trips pass 21 lines
// glaurung: while_zero_trips @ 0x1100
int32_t while_zero_trips(int32_t arg0) {
    int index;
    int total;
    long var6;
    int var8;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return 0;
    }
    index = 0;
    var6 = 0;
    do {
        var6 = (unsigned long)((unsigned int)((var6 + index)));
        var8 = (index + 1);
        index = (unsigned long)((unsigned int)(var8));
    } while (((unsigned int)(arg0) != (unsigned int)(var8)));
    return (unsigned int)((arg0 + ((unsigned long)((unsigned int)((var6 + (var6 * 4)))) * 2)));
}

← 213 fixtures