Fixture 107

short circuit

C · 3 functions · 4 lanes · 12 of 12 function-lanes behave identically

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

&& and || impose a sequence point and evaluate the right operand only when needed. Counting the evaluations proves the branch was preserved rather than flattened into an arithmetic combination.

tests/decompiler_fixtures/src/107_short_circuit.c source
#include <stdint.h>

/* && and || impose a sequence point and evaluate the right operand only when
 * needed. Counting the evaluations proves the branch was preserved rather than
 * flattened into an arithmetic combination. */

static int32_t observe(int32_t value, int32_t *counter) {
    *counter += 1;
    return value;
}

__attribute__((noinline)) int32_t
short_circuit_and(int32_t a, int32_t b, int32_t *evaluations) {
    if (evaluations == 0) {
        return -1;
    }
    *evaluations = 0;
    if (observe(a, evaluations) && observe(b, evaluations)) {
        return 1;
    }
    return 0;
}

__attribute__((noinline)) int32_t
short_circuit_or(int32_t a, int32_t b, int32_t *evaluations) {
    if (evaluations == 0) {
        return -1;
    }
    *evaluations = 0;
    if (observe(a, evaluations) || observe(b, evaluations)) {
        return 1;
    }
    return 0;
}

__attribute__((noinline)) int32_t
guarded_dereference(const int32_t *pointer, int32_t index, int32_t bound) {
    /* The classic guard: the right operand must not run when the left fails. */
    if (pointer != 0 && index >= 0 && index < bound && bound <= 16) {
        return pointer[index];
    }
    return -1;
}

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

3/3
guarded_dereference pass 28 lines
// glaurung: guarded_dereference @ 0x1220
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    int local_4;
    // x86-64 prologue: save rbp
    if ((arg0 == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((long)(arg1) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if ((arg2 <= arg1)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    local_4 = arg0[(long)(arg1)];
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
short_circuit_and pass 23 lines
// glaurung: short_circuit_and @ 0x1100
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
    extern int observe(int, int *);
    int var1;
    int var3;
    // x86-64 prologue: save rbp, frame 32 bytes
    if ((arg2 != 0)) {
        *(int *)((long)arg2) = 0;
        var1 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
        if (((unsigned long)((unsigned int)(var1)) == 0)) {
            // x86-64 epilogue: restore rbp
            return 0;
        }
        var3 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
        if (((unsigned long)((unsigned int)(var3)) == 0)) {
            // x86-64 epilogue: restore rbp
            return 0;
        }
        return 1;
    } else {
        return (unsigned int)(-1);
    }
}
short_circuit_or pass 25 lines
// glaurung: short_circuit_or @ 0x11a0
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
    extern int observe(int, int *);
    int local_4;
    int var1;
    int var3;
    if ((arg2 == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    *(int *)((long)arg2) = 0;
    var1 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
    if (((unsigned long)((unsigned int)(var1)) == 0)) {
        var3 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
        if (((unsigned long)((unsigned int)(var3)) == 0)) {
            goto L_1209;
        }
    }
    // x86-64 epilogue: restore rbp
    return 1;
    L_1209: ;
    // x86-64 epilogue: restore rbp
    return 0;
}

clang -O2

3/3
guarded_dereference pass 17 lines
// glaurung: guarded_dereference @ 0x1160
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    long ret;
    ret = 0xffffffff;
    if ((arg0 != 0)) {
        if (((long)(arg1) < 0)) {
            return ret;
        }
        if ((arg2 <= arg1)) {
            return ret;
        }
        if (((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) != 0)) {
            ret = (unsigned long)((unsigned int)(arg0[(unsigned long)((unsigned int)(arg1))]));
        }
    }
    return ret;
}
short_circuit_and pass 16 lines
// glaurung: short_circuit_and @ 0x1100
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
    long ret;
    if ((arg2 == 0)) {
        return 0xffffffff;
    }
    *(int *)(((long)arg2)) = 1;
    if (((unsigned long)((unsigned int)(arg0)) != 0)) {
        *(int *)(((long)arg2)) = 2;
        ret = 1;
        if (((unsigned long)((unsigned int)(arg1)) != 0)) {
            return ret;
        }
    }
    return 0;
}
short_circuit_or pass 14 lines
// glaurung: short_circuit_or @ 0x1130
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
    long ret;
    if ((arg2 == 0)) {
        return 0xffffffff;
    }
    *(int *)(((long)arg2)) = 1;
    ret = 1;
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        *(int *)(((long)arg2)) = 2;
        return ((unsigned long)((unsigned int)(arg1)) != 0);
    }
    return ret;
}

gcc -O0

3/3
guarded_dereference pass 22 lines
// glaurung: guarded_dereference @ 0x11e8
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    // x86-64 prologue: save rbp
    if ((arg0 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((long)(arg1) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if ((arg2 <= arg1)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(arg0[(long)(arg1)]);
}
short_circuit_and pass 23 lines
// glaurung: short_circuit_and @ 0x111c
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
    extern int observe(int, int *);
    int var3;
    int var7;
    // x86-64 prologue: save rbp, frame 16 bytes
    if ((arg2 != 0)) {
        *(int *)((long)arg2) = 0;
        var3 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
        if (((unsigned long)((unsigned int)(var3)) == 0)) {
            // x86-64 epilogue: restore rbp
            return 0;
        }
        var7 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
        if (((unsigned long)((unsigned int)(var7)) == 0)) {
            // x86-64 epilogue: restore rbp
            return 0;
        }
        return 1;
    } else {
        return 0xffffffff;
    }
}
short_circuit_or pass 23 lines
// glaurung: short_circuit_or @ 0x1182
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
    extern int observe(int, int *);
    int var3;
    int var7;
    if ((arg2 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    *(int *)((long)arg2) = 0;
    var3 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
    if (((unsigned long)((unsigned int)(var3)) != 0)) {
        // x86-64 epilogue: restore rbp
        return 1;
    }
    var7 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
    if (((unsigned long)((unsigned int)(var7)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    // x86-64 epilogue: restore rbp
    return 1;
}

gcc -O2

3/3
guarded_dereference pass 13 lines
// glaurung: guarded_dereference @ 0x1180
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    if ((arg0 == 0)) {
        return 0xffffffff;
    }
    if (((long)(arg1) < 0)) {
        return 0xffffffff;
    }
    if (((arg2 <= arg1) || ((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) == 0))) {
        return 0xffffffff;
    }
    return (unsigned int)(arg0[(long)(arg1)]);
}
short_circuit_and pass 12 lines
// glaurung: short_circuit_and @ 0x1100
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
    if ((arg2 == 0)) {
        return 0xffffffff;
    }
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        *(int *)(((long)arg2)) = 1;
        return 0;
    }
    *(int *)(((long)arg2)) = 2;
    return ((unsigned long)((unsigned int)(arg1)) != 0);
}
short_circuit_or pass 12 lines
// glaurung: short_circuit_or @ 0x1140
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
    if ((arg2 == 0)) {
        return 0xffffffff;
    }
    if (((unsigned long)((unsigned int)(arg0)) != 0)) {
        *(int *)(((long)arg2)) = 1;
        return 1;
    }
    *(int *)(((long)arg2)) = 2;
    return ((unsigned long)((unsigned int)(arg1)) != 0);
}

← 213 fixtures