Fixture 126

x macros

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

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

X-macros: one list expanded into several different constructs. The source has a single point of truth, but the binary contains a table, a switch and a sum with no trace of the macro that generated them.

tests/decompiler_fixtures/src/126_x_macros.c source
#include <stdint.h>

/* X-macros: one list expanded into several different constructs. The source
 * has a single point of truth, but the binary contains a table, a switch and a
 * sum with no trace of the macro that generated them. */

#define OPCODE_LIST(X) \
    X(NOP, 0, 1)       \
    X(INC, 1, 2)       \
    X(DEC, 2, 3)       \
    X(SHL, 3, 5)       \
    X(NEG, 4, 8)

#define AS_ENUM(name, value, weight) OPCODE_##name = (value),
enum Opcode { OPCODE_LIST(AS_ENUM) OPCODE_COUNT = 5 };
#undef AS_ENUM

#define AS_WEIGHT(name, value, weight) [value] = (weight),
static const int32_t OPCODE_WEIGHTS[5] = {OPCODE_LIST(AS_WEIGHT)};
#undef AS_WEIGHT

__attribute__((noinline)) int32_t opcode_weight(int32_t opcode) {
    if (opcode < 0 || opcode >= (int32_t)OPCODE_COUNT) {
        return -1;
    }
    return OPCODE_WEIGHTS[opcode];
}

__attribute__((noinline)) int32_t apply_opcode(int32_t opcode, int32_t value) {
    switch (opcode) {
#define AS_CASE(name, value_, weight) case OPCODE_##name:
        AS_CASE(NOP, 0, 1)
        return value;
        AS_CASE(INC, 1, 2)
        return (int32_t)((uint32_t)value + 1u);
        AS_CASE(DEC, 2, 3)
        return (int32_t)((uint32_t)value - 1u);
        AS_CASE(SHL, 3, 5)
        return (int32_t)((uint32_t)value << 1);
        AS_CASE(NEG, 4, 8)
        return (int32_t)(0u - (uint32_t)value);
#undef AS_CASE
    default:
        return -1;
    }
}

__attribute__((noinline)) int32_t total_weight(void) {
    int32_t total = 0;
#define AS_SUM(name, value, weight) total += (weight);
    OPCODE_LIST(AS_SUM)
#undef AS_SUM
    return total;
}

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

2/3
apply_opcode pass 19 lines
// glaurung: apply_opcode @ 0x1140
int32_t apply_opcode(int32_t arg0, int32_t arg1) {
    // x86-64 prologue: save rbp
    switch ((unsigned int)(arg0)) {
        case 0:
            return (unsigned int)(arg1);
        case 1:
            return (unsigned int)(((unsigned int)(arg1) + 1));
        case 2:
            return (unsigned int)(((unsigned int)(arg1) - 1));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) << 1));
        case 4:
            return (unsigned int)((0 - arg1));
        default:
            return (unsigned int)(-1);
    }
    // x86-64 epilogue: restore rbp
}
opcode_weight fail 18 lines
// glaurung: opcode_weight @ 0x1100
int32_t opcode_weight(int32_t arg0) {
    int local_4;
    // x86-64 prologue: save rbp
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if ((5 <= (long)(arg0))) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    local_4 = *(int *)((0x2020 + ((long)(arg0) * 4)));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
total_weight pass 13 lines
// glaurung: total_weight @ 0x11c0
int32_t total_weight(void) {
    int total;
    // x86-64 prologue: save rbp
    total = 0;
    total = ((unsigned int)(total) + 1);
    total = ((unsigned int)(total) + 2);
    total = ((unsigned int)(total) + 3);
    total = ((unsigned int)(total) + 5);
    total = ((unsigned int)(total) + 8);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(total);
}

clang -O2

2/3
apply_opcode fail 7 lines
// glaurung: apply_opcode @ 0x1120
int32_t apply_opcode(int32_t arg0, int32_t arg1) {
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(4))) {
        /* unrecovered indirect jump through ((long)((int)((((unsigned long)((unsigned int)(arg0)) == 0) ? 0xfffff142 : (((unsigned long)((unsigned int)(arg0)) == 1) ? 0xfffff139 : (((unsigned long)((unsigned int)(arg0)) == 2) ? 0xfffff143 : (((unsigned long)((unsigned int)(arg0)) == 3) ? 0xfffff147 : (((unsigned long)((unsigned int)(arg0)) == 4) ? 0xfffff14a : *(int *)((0x2000 + ((unsigned long)((unsigned int)(arg0)) * 4)))))))))) + 0x2000) */
    }
    return 0xffffffff;
}
opcode_weight pass 9 lines
// glaurung: opcode_weight @ 0x1100
int32_t opcode_weight(int32_t arg0) {
    long ret;
    ret = 0xffffffff;
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(4))) {
        ret = (unsigned long)((unsigned int)((((unsigned long)((unsigned int)(arg0)) == 0) ? 1 : (((unsigned long)((unsigned int)(arg0)) == 1) ? 2 : (((unsigned long)((unsigned int)(arg0)) == 2) ? 3 : (((unsigned long)((unsigned int)(arg0)) == 3) ? 5 : (((unsigned long)((unsigned int)(arg0)) == 4) ? 8 : *(int *)((0x2020 + ((unsigned long)((unsigned int)(arg0)) * 4))))))))));
    }
    return ret;
}
total_weight pass 4 lines
// glaurung: total_weight @ 0x1150
int32_t total_weight(void) {
    return 19;
}

gcc -O0

2/3
apply_opcode pass 19 lines
// glaurung: apply_opcode @ 0x1130
int32_t apply_opcode(int32_t arg0, int32_t arg1) {
    // x86-64 prologue: save rbp
    switch ((unsigned long)((unsigned int)(arg0))) {
        case 0:
            return (unsigned int)(arg1);
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + 1));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) - 1));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + (unsigned long)((unsigned int)(arg1))));
        case 4:
            return (-(unsigned long)((unsigned int)(arg1)));
        default:
            return 0xffffffff;
    }
    // x86-64 epilogue: restore rbp
}
opcode_weight fail 14 lines
// glaurung: opcode_weight @ 0x10f9
int32_t opcode_weight(int32_t arg0) {
    // x86-64 prologue: save rbp
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 4) | ((long)(arg0) < 4)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)((((long)(arg0) * 4) + 0x2000)));
}
total_weight pass 13 lines
// glaurung: total_weight @ 0x1192
int32_t total_weight(void) {
    int total;
    // x86-64 prologue: save rbp
    total = 0;
    total = (total + 1);
    total = (total + 2);
    total = (total + 3);
    total = (total + 5);
    total = (total + 8);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(total);
}

gcc -O2

3/3
apply_opcode pass 17 lines
// glaurung: apply_opcode @ 0x1130
int32_t apply_opcode(int32_t arg0, int32_t arg1) {
    switch ((unsigned long)((unsigned int)(arg0))) {
        case 0:
            return (unsigned int)(arg1);
        case 1:
            return (unsigned int)((arg1 + 1));
        case 2:
            return (unsigned int)((arg1 - 1));
        case 3:
            return (unsigned int)((arg1 + arg1));
        case 4:
            return (-(unsigned long)((unsigned int)(arg1)));
        default:
            return (unsigned int)(-1);
    }
}
opcode_weight pass 7 lines
// glaurung: opcode_weight @ 0x1100
int32_t opcode_weight(int32_t arg0) {
    if (((unsigned long)(4) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    return (unsigned int)(((long)(arg0) == 0) ? 1 : (((long)(arg0) == 1) ? 2 : (((long)(arg0) == 2) ? 3 : (((long)(arg0) == 3) ? 5 : (((long)(arg0) == 4) ? 8 : *(int *)((0x2020 + ((long)(arg0) * 4))))))));
}
total_weight pass 5 lines
// glaurung: total_weight @ 0x1180
int32_t total_weight(void) {
    int total;
    return 19;
}

← 213 fixtures