Fixture 103

computed goto

C · 1 functions · 4 lanes · 0 of 4 function-lanes behave identically

4 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (0/1), clang-O2 (0/1), gcc-O0 (0/1), gcc-O2 (0/1).

Labels as values (a GCC extension both compilers implement): a jump table the source builds explicitly. The dispatch is an indirect branch to an address loaded from an array, which is exactly the shape a decompiler must not mistake for a compiler-generated switch table.

tests/decompiler_fixtures/src/103_computed_goto.c source
#include <stdint.h>

/* Labels as values (a GCC extension both compilers implement): a jump table the
 * source builds explicitly. The dispatch is an indirect branch to an address
 * loaded from an array, which is exactly the shape a decompiler must not
 * mistake for a compiler-generated switch table. */

#define TOKEN_MAX 16

__attribute__((noinline)) int32_t
threaded_interpreter(const int32_t *program, int32_t length, int32_t seed) {
    static void *const targets[4] = {&&do_add, &&do_double, &&do_negate,
                                     &&do_clamp};
    int32_t accumulator = seed;
    int32_t position = 0;
    if (program == 0 || length < 0 || length > TOKEN_MAX) {
        return -1;
    }
    while (position < length) {
        int32_t opcode = program[position] & 3;
        position += 1;
        goto *targets[opcode];
    do_add:
        accumulator += position;
        continue;
    do_double:
        accumulator = (int32_t)((uint32_t)accumulator * 2u);
        continue;
    do_negate:
        accumulator = (int32_t)(0u - (uint32_t)accumulator);
        continue;
    do_clamp:
        if (accumulator > 1000) {
            accumulator = 1000;
        }
        continue;
    }
    return accumulator;
}

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

0/1
threaded_interpreter fail 34 lines
// glaurung: threaded_interpreter @ 0x1100
int32_t threaded_interpreter(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    int accumulator;
    int position;
    int opcode;
    long local_30;
    int local_4;
    accumulator = arg2;
    position = 0;
    if ((arg0 != 0)) {
        if ((0 <= (long)(arg1))) {
            if ((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16))) {
                goto L_1146;
            }
        }
    }
    local_4 = -1;
    goto L_11d1;
    L_1146: ;
    goto L_114b;
    L_114b: ;
    if ((position < arg1)) {
        opcode = ((unsigned int)(arg0[(long)(position)]) & 3);
        position = ((unsigned int)(position) + 1);
        local_30 = *(long *)((0x3e30 + ((long)(opcode) * 8)));
        goto L_11d6;
    }
    local_4 = accumulator;
    L_11d1: ;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
    L_11d6: ;
    /* unrecovered indirect jump through local_30 */
}

clang -O2

0/1
threaded_interpreter fail 17 lines
// glaurung: threaded_interpreter @ 0x1100
int32_t threaded_interpreter(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    int opcode;
    int position;
    long ret;
    ret = 0xffffffff;
    if ((arg0 == 0)) {
        return ret;
    }
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return ret;
    }
    if (((unsigned long)((unsigned int)(arg1)) == 0)) {
        return (unsigned int)(arg2);
    }
    /* unrecovered indirect jump through *(long *)((0x3e30 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)((long)arg0))) & 3))) * 8))) */
}

gcc -O0

0/1
threaded_interpreter fail 28 lines
// glaurung: threaded_interpreter @ 0x10f9
int32_t threaded_interpreter(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    int accumulator;
    int position;
    int opcode;
    // x86-64 prologue: save rbp
    accumulator = arg2;
    position = 0;
    if ((arg0 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((long)(arg1) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if ((position < arg1)) {
        opcode = ((unsigned int)(arg0[(long)(position)]) & 3);
        position = (position + 1);
        /* unrecovered indirect jump through *(long *)((((long)(opcode) * 8) + 0x3e60)) */
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(accumulator);
}

gcc -O2

0/1
threaded_interpreter fail 16 lines
// glaurung: threaded_interpreter @ 0x1100
int32_t threaded_interpreter(const int32_t * arg0, int32_t arg1, int32_t arg2) {
    int accumulator;
    int position;
    if ((arg0 == 0)) {
        return 0xffffffff;
    }
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return 0xffffffff;
    }
    accumulator = (unsigned long)((unsigned int)(arg2));
    if (((unsigned long)((unsigned int)(arg1)) != 0)) {
        /* unrecovered indirect jump through *(long *)((0x3e60 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(((long)arg0)))) & 3))) * 8))) */
    }
    return accumulator;
}

← 213 fixtures