Fixture 204

adjacent dispatch tables

C · 5 functions · 4 lanes · 19 of 20 function-lanes behave identically

One lane has a function that returns a different result after decompilation: clang-O2 (4/5).

Several guard-free switch dispatches in ONE translation unit, so their jump tables land ADJACENT in .rodata.

WHY THIS SHAPE HAS ITS OWN FIXTURE. analysis::jump_table:: discover_jump_tables recovers a table by taking the LONGEST run of section words that decode to executable addresses. That rule has no end marker: when a second table begins immediately after the first, the scan runs straight through the boundary and reports one table of N1 + N2 + ... entries. The over-read entries are table 2's offsets read relative to table 1's base, so they resolve to T_j - 4*N1 — addresses that are still inside .text and therefore still pass in_exec_regions, which is why nothing downstream rejects them.

cfg::discover_function has exactly one defence against that: a dispatch whose index carries no range check is attached SPECULATIVELY (TentativeDispatchEdges::needs_bound_proof), and a whole-CFG must-dataflow fixed point then has to prove a prefix, after which trim_unproven_dispatch_edges deletes the rest. Both halves of that only matter when the over-read actually happens, and it only happens when two tables abut. 148_dispatch_obfuscation has a single permuted switch, so its table is the last thing in its .rodata neighbourhood and the scan stops on its own; measured over the whole corpus at gcc/clang x O0/O1/O2 (1,107 binaries) the bound-proof path fires 8 times and the trim fires ZERO times. This file is the missing input: it makes the tables abut on purpose.

Each selector goes through a byte permutation table, which is what stops the compiler emitting its own cmp key, N; ja default range check — a value loaded from a uint8_t array is already provably in [0, 255] and, after the compiler propagates the table contents, in [0, 7]. That is what makes the dispatch guard-free, and a guard-free dispatch is the only kind that reaches the bound-proof path at all.

The permutations are deliberately not the identity and not monotone, so a recovery that reconstructs the table but drops the mapping produces a well-structured program that computes the wrong case for every selector.

Safety: every index is masked to the map's size before the load, every map entry is less than the number of cases, and all arithmetic runs through uint32_t, so no input can overflow a signed type or read out of bounds.

tests/decompiler_fixtures/src/204_adjacent_dispatch_tables.c source
/* Several guard-free `switch` dispatches in ONE translation unit, so their jump
 * tables land ADJACENT in `.rodata`.
 *
 * WHY THIS SHAPE HAS ITS OWN FIXTURE. `analysis::jump_table::
 * discover_jump_tables` recovers a table by taking the LONGEST run of section
 * words that decode to executable addresses. That rule has no end marker: when
 * a second table begins immediately after the first, the scan runs straight
 * through the boundary and reports one table of `N1 + N2 + ...` entries. The
 * over-read entries are table 2's offsets read relative to table 1's base, so
 * they resolve to `T_j - 4*N1` — addresses that are still inside `.text` and
 * therefore still pass `in_exec_regions`, which is why nothing downstream
 * rejects them.
 *
 * `cfg::discover_function` has exactly one defence against that: a dispatch
 * whose index carries no range check is attached SPECULATIVELY
 * (`TentativeDispatchEdges::needs_bound_proof`), and a whole-CFG must-dataflow
 * fixed point then has to prove a prefix, after which
 * `trim_unproven_dispatch_edges` deletes the rest. Both halves of that only
 * matter when the over-read actually happens, and it only happens when two
 * tables abut. `148_dispatch_obfuscation` has a single permuted switch, so its
 * table is the last thing in its `.rodata` neighbourhood and the scan stops on
 * its own; measured over the whole corpus at gcc/clang x O0/O1/O2 (1,107
 * binaries) the bound-proof path fires 8 times and the trim fires ZERO times.
 * This file is the missing input: it makes the tables abut on purpose.
 *
 * Each selector goes through a byte permutation table, which is what stops the
 * compiler emitting its own `cmp key, N; ja default` range check — a value
 * loaded from a `uint8_t` array is already provably in `[0, 255]` and, after
 * the compiler propagates the table contents, in `[0, 7]`. That is what makes
 * the dispatch guard-free, and a guard-free dispatch is the only kind that
 * reaches the bound-proof path at all.
 *
 * The permutations are deliberately not the identity and not monotone, so a
 * recovery that reconstructs the table but drops the mapping produces a
 * well-structured program that computes the wrong case for every selector.
 *
 * Safety: every index is masked to the map's size before the load, every map
 * entry is less than the number of cases, and all arithmetic runs through
 * `uint32_t`, so no input can overflow a signed type or read out of bounds.
 */

#include <stdint.h>

#define ADT204_MAP_SIZE 8u

/* Three distinct permutations of 0..7. Distinct so that a decompiler cannot
 * satisfy all three switches with one recovered mapping. */
static const uint8_t ADT204_MAP_A[ADT204_MAP_SIZE] = {5, 3, 7, 1, 6, 0, 4, 2};
static const uint8_t ADT204_MAP_B[ADT204_MAP_SIZE] = {2, 6, 0, 4, 1, 7, 3, 5};
static const uint8_t ADT204_MAP_C[ADT204_MAP_SIZE] = {7, 1, 5, 3, 0, 6, 2, 4};

/* First of the three abutting tables. Its scan is the one that runs off the
 * end into the other two. */
__attribute__((noinline)) int32_t adt204_switch_a(int32_t selector, int32_t value) {
    uint32_t key = ADT204_MAP_A[(uint32_t)selector & (ADT204_MAP_SIZE - 1u)];
    uint32_t v = (uint32_t)value;

    switch (key) {
    case 0u:
        return (int32_t)(v + 1u);
    case 1u:
        return (int32_t)(v * 2u);
    case 2u:
        return (int32_t)(v ^ 0x5a5au);
    case 3u:
        return (int32_t)(v >> 1);
    case 4u:
        return (int32_t)(v - 7u);
    case 5u:
        return (int32_t)(v & 0xffu);
    case 6u:
        return (int32_t)(v | 0x100u);
    default:
        return (int32_t)(0u - v);
    }
}

/* Second table. Its entries are what the first table's over-read misreads. */
__attribute__((noinline)) int32_t adt204_switch_b(int32_t selector, int32_t value) {
    uint32_t key = ADT204_MAP_B[(uint32_t)selector & (ADT204_MAP_SIZE - 1u)];
    uint32_t v = (uint32_t)value;

    switch (key) {
    case 0u:
        return (int32_t)(v + 11u);
    case 1u:
        return (int32_t)(v * 3u);
    case 2u:
        return (int32_t)(v ^ 0x1234u);
    case 3u:
        return (int32_t)(v >> 2);
    case 4u:
        return (int32_t)(v - 17u);
    case 5u:
        return (int32_t)(v & 0xfffu);
    case 6u:
        return (int32_t)(v | 0x200u);
    default:
        return (int32_t)(1u - v);
    }
}

/* Third table, so the over-read has to cross two boundaries rather than one.
 * Two boundaries matter: a trim that keeps "everything up to the first
 * discontinuity" and a trim that keeps a proven prefix behave identically with
 * one boundary and differently with two. */
__attribute__((noinline)) int32_t adt204_switch_c(int32_t selector, int32_t value) {
    uint32_t key = ADT204_MAP_C[(uint32_t)selector & (ADT204_MAP_SIZE - 1u)];
    uint32_t v = (uint32_t)value;

    switch (key) {
    case 0u:
        return (int32_t)(v + 21u);
    case 1u:
        return (int32_t)(v * 5u);
    case 2u:
        return (int32_t)(v ^ 0x4321u);
    case 3u:
        return (int32_t)(v >> 3);
    case 4u:
        return (int32_t)(v - 27u);
    case 5u:
        return (int32_t)(v & 0xffffu);
    case 6u:
        return (int32_t)(v | 0x400u);
    default:
        return (int32_t)(2u - v);
    }
}

/* Same case bodies as `adt204_switch_a`, but the selector is range-checked in
 * the source rather than through a permutation table. Its role is lane
 * dependent, and that is the useful part.
 *
 * At gcc it is the NEGATIVE CONTROL: gcc emits `cmp sel,7; ja default` with the
 * dispatch on the fall-through, which is the one edge polarity
 * `cfg::ctrl_flow::guard_bound_reaches_fallthrough` models, so the switch
 * resolves and a change that breaks this function damaged ordinary guarded
 * switches rather than the speculative path it was aimed at.
 *
 * At clang it is a SECOND POSITIVE, for a different reason than the three
 * above: clang reaches the dispatch on the TAKEN edge of a `jb`, and the taken
 * edge of a below/below-or-equal branch is not modelled at all — only the
 * fall-through of `ja`/`jae` is. Same source, same guard, opposite branch
 * polarity, and the bound is lost. That asymmetry is why a dispatch block with
 * several `jbe` predecessors (measured in `/usr/bin/3cpio` at 0x2f4bf,
 * `/usr/bin/aarch64-linux-gnu-ld.bfd` at 0x18119 and
 * `/usr/bin/aarch64-linux-gnu-objdump` at 0x20c21) currently gets its bound
 * from ONE predecessor's fall-through and nothing from the others. */
__attribute__((noinline)) int32_t adt204_guarded_control(int32_t selector, int32_t value) {
    uint32_t v = (uint32_t)value;

    if (selector < 0 || selector > 7) {
        return -1;
    }
    switch ((uint32_t)selector) {
    case 0u:
        return (int32_t)(v + 1u);
    case 1u:
        return (int32_t)(v * 2u);
    case 2u:
        return (int32_t)(v ^ 0x5a5au);
    case 3u:
        return (int32_t)(v >> 1);
    case 4u:
        return (int32_t)(v - 7u);
    case 5u:
        return (int32_t)(v & 0xffu);
    case 6u:
        return (int32_t)(v | 0x100u);
    default:
        return (int32_t)(0u - v);
    }
}

/* Drives all three abutting dispatches from one selector, so a recovery that
 * loses the arms of only the FIRST table (the one the scan over-reads from)
 * still produces an observably different result. */
__attribute__((noinline)) int32_t adt204_chained(int32_t selector, int32_t value) {
    int32_t a = adt204_switch_a(selector, value);
    int32_t b = adt204_switch_b(selector, a);
    int32_t c = adt204_switch_c(selector, b);
    return (int32_t)((uint32_t)c ^ (uint32_t)a);
}

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 -O2

4/5
adt204_chained pass 21 lines
// glaurung: adt204_chained @ 0x1280
int32_t adt204_chained(int32_t arg0, int32_t arg1) {
    extern int adt204_switch_a(int, int);
    extern int adt204_switch_b(int, int);
    extern int adt204_switch_c(int, int);
    int a;
    int b;
    int c;
    long var0;
    int var1;
    int var4;
    int var6;
    // x86-64 prologue: save callee registers, frame 24 bytes
    var0 = (unsigned long)((unsigned int)(arg0));
    var1 = adt204_switch_a(arg0, arg1);
    a = (unsigned long)((unsigned int)(var1));
    var4 = adt204_switch_b((unsigned long)((unsigned int)(var0)), (unsigned long)((unsigned int)(var1)));
    var6 = adt204_switch_c((unsigned long)((unsigned int)(var0)), (unsigned long)((unsigned int)(var4)));
    // x86-64 epilogue: restore callee registers
    return (unsigned int)((var6 ^ a));
}
adt204_guarded_control fail 12 lines
// glaurung: adt204_guarded_control @ 0x1230
int32_t adt204_guarded_control(int32_t arg0, int32_t arg1) {
    long ret;
    ret = 0xffffffff;
    if (((unsigned long)(7) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return ret;
    }
    if (((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)(7))) {
        /* unrecovered indirect jump through ((long)((int)((((unsigned long)((unsigned int)(arg0)) == 0) ? 0xfffff1fa : (((unsigned long)((unsigned int)(arg0)) == 1) ? 0xfffff203 : (((unsigned long)((unsigned int)(arg0)) == 2) ? 0xfffff207 : (((unsigned long)((unsigned int)(arg0)) == 3) ? 0xfffff20f : (((unsigned long)((unsigned int)(arg0)) == 4) ? 0xfffff213 : (((unsigned long)((unsigned int)(arg0)) == 5) ? 0xfffff218 : (((unsigned long)((unsigned int)(arg0)) == 6) ? 0xfffff21d : *(int *)((0x2054 + ((unsigned long)((unsigned int)(arg0)) * 4)))))))))))) + 0x2054) */
    }
    return (unsigned int)((-arg1));
}
adt204_switch_a pass 21 lines
// glaurung: adt204_switch_a @ 0x1130
int32_t adt204_switch_a(int32_t arg0, int32_t arg1) {
    switch ((unsigned int)((unsigned char)((((unsigned long)((unsigned int)((arg0 & 7))) == 0) ? 5 : (((unsigned long)((unsigned int)((arg0 & 7))) == 1) ? 3 : (((unsigned long)((unsigned int)((arg0 & 7))) == 2) ? 7 : (((unsigned long)((unsigned int)((arg0 & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)((arg0 & 7))) == 4) ? 6 : (((unsigned long)((unsigned int)((arg0 & 7))) == 5) ? 0 : (((unsigned long)((unsigned int)((arg0 & 7))) == 6) ? 4 : (((unsigned long)((unsigned int)((arg0 & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)((arg0 & 7))) + 0x2070))))))))))))) {
        case 0:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + 1));
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + (unsigned long)((unsigned int)(arg1))));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 1));
        case 4:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) - 7));
        case 5:
            return (unsigned int)((unsigned char)(((unsigned long)((unsigned int)(arg1)) & 255)));
        case 6:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) | 256));
        default:
            return (-(unsigned long)((unsigned int)(arg1)));
    }
}
adt204_switch_b pass 21 lines
// glaurung: adt204_switch_b @ 0x1180
int32_t adt204_switch_b(int32_t arg0, int32_t arg1) {
    switch ((unsigned int)((unsigned char)((((unsigned long)((unsigned int)((arg0 & 7))) == 0) ? 2 : (((unsigned long)((unsigned int)((arg0 & 7))) == 1) ? 6 : (((unsigned long)((unsigned int)((arg0 & 7))) == 2) ? 0 : (((unsigned long)((unsigned int)((arg0 & 7))) == 3) ? 4 : (((unsigned long)((unsigned int)((arg0 & 7))) == 4) ? 1 : (((unsigned long)((unsigned int)((arg0 & 7))) == 5) ? 7 : (((unsigned long)((unsigned int)((arg0 & 7))) == 6) ? 3 : (((unsigned long)((unsigned int)((arg0 & 7))) == 7) ? 5 : *(char *)(((unsigned long)((unsigned int)((arg0 & 7))) + 0x2078))))))))))))) {
        case 0:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + 11));
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + ((unsigned long)((unsigned int)(arg1)) * 2)));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x1234));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 2));
        case 4:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) - 17));
        case 5:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) & 4095));
        case 6:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) | 512));
        default:
            return (unsigned int)((1 - (unsigned long)((unsigned int)(arg1))));
    }
}
adt204_switch_c pass 21 lines
// glaurung: adt204_switch_c @ 0x11e0
int32_t adt204_switch_c(int32_t arg0, int32_t arg1) {
    switch ((unsigned int)((unsigned char)((((unsigned long)((unsigned int)((arg0 & 7))) == 0) ? 7 : (((unsigned long)((unsigned int)((arg0 & 7))) == 1) ? 1 : (((unsigned long)((unsigned int)((arg0 & 7))) == 2) ? 5 : (((unsigned long)((unsigned int)((arg0 & 7))) == 3) ? 3 : (((unsigned long)((unsigned int)((arg0 & 7))) == 4) ? 0 : (((unsigned long)((unsigned int)((arg0 & 7))) == 5) ? 6 : (((unsigned long)((unsigned int)((arg0 & 7))) == 6) ? 2 : (((unsigned long)((unsigned int)((arg0 & 7))) == 7) ? 4 : *(char *)(((unsigned long)((unsigned int)((arg0 & 7))) + 0x2080))))))))))))) {
        case 0:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + 21));
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) + ((unsigned long)((unsigned int)(arg1)) * 4)));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x4321));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 3));
        case 4:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) - 27));
        case 5:
            return (unsigned int)((unsigned short)(((unsigned long)((unsigned int)(arg1)) & 0xffff)));
        case 6:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) | 1024));
        default:
            return (unsigned int)((2 - (unsigned long)((unsigned int)(arg1))));
    }
}

clang -O0

5/5
adt204_chained pass 21 lines
// glaurung: adt204_chained @ 0x1440
int32_t adt204_chained(int32_t arg0, int32_t arg1) {
    extern int adt204_switch_a(int, int);
    extern int adt204_switch_b(int, int);
    extern int adt204_switch_c(int, int);
    int a;
    int b;
    int c;
    int var0;
    int var2;
    int var4;
    // x86-64 prologue: save rbp, frame 32 bytes
    var0 = adt204_switch_a((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
    a = var0;
    var2 = adt204_switch_b((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(a)));
    b = var2;
    var4 = adt204_switch_c((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(b)));
    c = var4;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(c)) ^ a));
}
adt204_guarded_control pass 36 lines
// glaurung: adt204_guarded_control @ 0x1370
int32_t adt204_guarded_control(int32_t arg0, int32_t arg1) {
    unsigned int v;
    int local_4;
    // x86-64 prologue: save rbp
    v = arg1;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 7) | ((long)(arg0) < 7)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    switch ((unsigned int)(arg0)) {
        case 0:
            return (unsigned int)((v + 1));
        case 1:
            return (unsigned int)((v << 1));
        case 2:
            return (unsigned int)((v ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned int)(v) >> 1));
        case 4:
            return (unsigned int)((v - 7));
        case 5:
            return (unsigned int)((v & 255));
        case 6:
            return (unsigned int)((v | 256));
        default:
            return (unsigned int)((0 - v));
    }
    // x86-64 epilogue: restore rbp
}
adt204_switch_a pass 27 lines
// glaurung: adt204_switch_a @ 0x1130
int32_t adt204_switch_a(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 5 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 4 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2070))))))))));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)((v + 1));
        case 1:
            return (unsigned int)((v << 1));
        case 2:
            return (unsigned int)((v ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned int)(v) >> 1));
        case 4:
            return (unsigned int)((v - 7));
        case 5:
            return (unsigned int)((v & 255));
        case 6:
            return (unsigned int)((v | 256));
        default:
            return (unsigned int)((0 - v));
    }
    // x86-64 epilogue: restore rbp
}
adt204_switch_b pass 27 lines
// glaurung: adt204_switch_b @ 0x11f0
int32_t adt204_switch_b(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 2 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 4 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 5 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2078))))))))));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)((v + 11));
        case 1:
            return (unsigned int)((v * 3));
        case 2:
            return (unsigned int)((v ^ 0x1234));
        case 3:
            return (unsigned int)(((unsigned int)(v) >> 2));
        case 4:
            return (unsigned int)((v - 17));
        case 5:
            return (unsigned int)((v & 4095));
        case 6:
            return (unsigned int)((v | 512));
        default:
            return (unsigned int)((1 - v));
    }
    // x86-64 epilogue: restore rbp
}
adt204_switch_c pass 27 lines
// glaurung: adt204_switch_c @ 0x12b0
int32_t adt204_switch_c(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 5 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 2 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 4 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2080))))))))));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)((v + 21));
        case 1:
            return (unsigned int)((v * 5));
        case 2:
            return (unsigned int)((v ^ 0x4321));
        case 3:
            return (unsigned int)(((unsigned int)(v) >> 3));
        case 4:
            return (unsigned int)((v - 27));
        case 5:
            return (unsigned int)((v & 0xffff));
        case 6:
            return (unsigned int)((v | 1024));
        default:
            return (unsigned int)((2 - v));
    }
    // x86-64 epilogue: restore rbp
}

gcc -O0

5/5
adt204_chained pass 21 lines
// glaurung: adt204_chained @ 0x13bf
int32_t adt204_chained(int32_t arg0, int32_t arg1) {
    extern int adt204_switch_a(int, int);
    extern int adt204_switch_b(int, int);
    extern int adt204_switch_c(int, int);
    int a;
    int b;
    int c;
    int var10;
    int var2;
    int var6;
    // x86-64 prologue: save rbp, frame 32 bytes
    var2 = adt204_switch_a((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
    a = var2;
    var6 = adt204_switch_b((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(a)));
    b = var6;
    var10 = adt204_switch_c((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(b)));
    c = var10;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(a)) ^ (unsigned long)((unsigned int)(c))));
}
adt204_guarded_control pass 33 lines
// glaurung: adt204_guarded_control @ 0x132e
int32_t adt204_guarded_control(int32_t arg0, int32_t arg1) {
    unsigned int v;
    // x86-64 prologue: save rbp
    v = arg1;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 7) | ((long)(arg0) < 7)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    switch ((unsigned long)((unsigned int)(arg0))) {
        case 0:
            return (unsigned int)((v + 1));
        case 1:
            return (unsigned int)((v + v));
        case 2:
            return (unsigned int)((v ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned int)(v) >> 1));
        case 4:
            return (unsigned int)((v - 7));
        case 5:
            return (unsigned int)((unsigned char)((v & 255)));
        case 6:
            return ((v & -0xff01LL) | ((((((unsigned int)(v) >> 8) & 255) | 1) & 255) << 8));
        default:
            return (-(unsigned long)(v));
    }
    // x86-64 epilogue: restore rbp
}
adt204_switch_a pass 27 lines
// glaurung: adt204_switch_a @ 0x1159
int32_t adt204_switch_a(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned int)((unsigned char)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 5 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 4 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 2 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2000)))))))))))) & 255));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)(((unsigned long)(v) + 1));
        case 1:
            return (unsigned int)(((unsigned long)(v) + (unsigned long)(v)));
        case 2:
            return (unsigned int)(((unsigned long)(v) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)(v) >> 1));
        case 4:
            return (unsigned int)(((unsigned long)(v) - 7));
        case 5:
            return (unsigned int)((unsigned char)(((unsigned long)(v) & 255)));
        case 6:
            return (((unsigned long)(v) & -0xff01LL) | ((((((unsigned long)(v) >> 8) & 255) | 1) & 255) << 8));
        default:
            return (-(unsigned long)(v));
    }
    // x86-64 epilogue: restore rbp
}
adt204_switch_b pass 27 lines
// glaurung: adt204_switch_b @ 0x11ef
int32_t adt204_switch_b(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned int)((unsigned char)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 2 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 4 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 5 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2008)))))))))))) & 255));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)(((unsigned long)(v) + 11));
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(v) + (unsigned long)(v)))) + (unsigned long)(v)));
        case 2:
            return (unsigned int)(((unsigned long)(v) ^ 0x1234));
        case 3:
            return (unsigned int)(((unsigned long)(v) >> 2));
        case 4:
            return (unsigned int)(((unsigned long)(v) - 17));
        case 5:
            return (unsigned int)(((unsigned long)(v) & 4095));
        case 6:
            return (((unsigned long)(v) & -0xff01LL) | ((((((unsigned long)(v) >> 8) & 255) | 2) & 255) << 8));
        default:
            return (unsigned int)((1 - v));
    }
    // x86-64 epilogue: restore rbp
}
adt204_switch_c pass 27 lines
// glaurung: adt204_switch_c @ 0x128f
int32_t adt204_switch_c(int32_t arg0, int32_t arg1) {
    unsigned int key;
    unsigned int v;
    // x86-64 prologue: save rbp
    key = (unsigned char)(((unsigned int)((unsigned char)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 0) ? 7 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 1) ? 1 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 2) ? 5 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 3) ? 3 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 4) ? 0 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 5) ? 6 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 6) ? 2 : (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) == 7) ? 4 : *(char *)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 7))) + 0x2010)))))))))))) & 255));
    v = arg1;
    switch (key) {
        case 0:
            return (unsigned int)(((unsigned long)(v) + 21));
        case 1:
            return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(v) << 2))) + (unsigned long)(v)));
        case 2:
            return (unsigned int)(((unsigned long)(v) ^ 0x4321));
        case 3:
            return (unsigned int)(((unsigned long)(v) >> 3));
        case 4:
            return (unsigned int)(((unsigned long)(v) - 27));
        case 5:
            return (unsigned int)((unsigned short)(((unsigned long)(v) & 0xffff)));
        case 6:
            return (((unsigned long)(v) & -0xff01LL) | ((((((unsigned long)(v) >> 8) & 255) | 4) & 255) << 8));
        default:
            return (unsigned int)((2 - v));
    }
    // x86-64 epilogue: restore rbp
}

gcc -O2

5/5
adt204_chained pass 20 lines
// glaurung: adt204_chained @ 0x1370
int32_t adt204_chained(int32_t arg0, int32_t arg1) {
    extern int adt204_switch_a(int, int);
    extern int adt204_switch_b(int, int);
    extern int adt204_switch_c(int, int);
    int a;
    int b;
    int c;
    long var0;
    int var1;
    int var4;
    int var6;
    var0 = (unsigned long)((unsigned int)(arg0));
    var1 = adt204_switch_a(arg0, arg1);
    a = (unsigned long)((unsigned int)(var1));
    var4 = adt204_switch_b((unsigned long)((unsigned int)(var0)), (unsigned long)((unsigned int)(var1)));
    var6 = adt204_switch_c((unsigned long)((unsigned int)(var0)), (unsigned long)((unsigned int)(var4)));
    // x86-64 epilogue: tear down frame
    return (unsigned int)((var6 ^ a));
}
adt204_guarded_control pass 24 lines
// glaurung: adt204_guarded_control @ 0x12f0
int32_t adt204_guarded_control(int32_t arg0, int32_t arg1) {
    if (((unsigned long)(7) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    switch ((unsigned long)((unsigned int)(arg0))) {
        case 0:
            return (unsigned int)((arg1 + 1));
        case 1:
            return (unsigned int)((arg1 + arg1));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 1));
        case 4:
            return (unsigned int)((arg1 - 7));
        case 5:
            return (unsigned int)((unsigned char)((arg1 & 255)));
        case 6:
            return (((unsigned long)((unsigned int)(arg1)) & -0xff01LL) | ((((((unsigned long)((unsigned int)(arg1)) >> 8) & 255) | 1) & 255) << 8));
        default:
            return (-(unsigned long)((unsigned int)(arg1)));
    }
}
adt204_switch_a pass 24 lines
// glaurung: adt204_switch_a @ 0x1160
int32_t adt204_switch_a(int32_t arg0, int32_t arg1) {
    long var2;
    var2 = (unsigned long)((unsigned int)((arg0 & 7)));
    if (((unsigned long)(6) < (unsigned long)((unsigned long)((unsigned char)(((var2 == 0) ? 5 : ((var2 == 1) ? 3 : ((var2 == 2) ? 7 : ((var2 == 3) ? 1 : ((var2 == 4) ? 6 : ((var2 == 5) ? 0 : ((var2 == 6) ? 4 : ((var2 == 7) ? 2 : *(char *)((0x2080 + var2))))))))))))))) {
        return (-(unsigned long)((unsigned int)(arg1)));
    }
    switch ((unsigned int)((unsigned char)(((var2 == 0) ? 5 : ((var2 == 1) ? 3 : ((var2 == 2) ? 7 : ((var2 == 3) ? 1 : ((var2 == 4) ? 6 : ((var2 == 5) ? 0 : ((var2 == 6) ? 4 : ((var2 == 7) ? 2 : *(char *)((0x2080 + var2))))))))))))) {
        case 0:
            return (unsigned int)((arg1 + 1));
        case 1:
            return (unsigned int)((arg1 + arg1));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x5a5a));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 1));
        case 4:
            return (unsigned int)((arg1 - 7));
        case 5:
            return (unsigned int)((unsigned char)((arg1 & 255)));
        case 6:
            return (((unsigned long)((unsigned int)(arg1)) & -0xff01LL) | ((((((unsigned long)((unsigned int)(arg1)) >> 8) & 255) | 1) & 255) << 8));
    }
}
adt204_switch_b pass 24 lines
// glaurung: adt204_switch_b @ 0x11e0
int32_t adt204_switch_b(int32_t arg0, int32_t arg1) {
    long var2;
    var2 = (unsigned long)((unsigned int)((arg0 & 7)));
    if (((unsigned long)(6) < (unsigned long)((unsigned long)((unsigned char)(((var2 == 0) ? 2 : ((var2 == 1) ? 6 : ((var2 == 2) ? 0 : ((var2 == 3) ? 4 : ((var2 == 4) ? 1 : ((var2 == 5) ? 7 : ((var2 == 6) ? 3 : ((var2 == 7) ? 5 : *(char *)((0x2078 + var2))))))))))))))) {
        return (unsigned int)((1 - arg1));
    }
    switch ((unsigned int)((unsigned char)(((var2 == 0) ? 2 : ((var2 == 1) ? 6 : ((var2 == 2) ? 0 : ((var2 == 3) ? 4 : ((var2 == 4) ? 1 : ((var2 == 5) ? 7 : ((var2 == 6) ? 3 : ((var2 == 7) ? 5 : *(char *)((0x2078 + var2))))))))))))) {
        case 0:
            return (unsigned int)((arg1 + 11));
        case 1:
            return (unsigned int)((arg1 + (arg1 * 2)));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x1234));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 2));
        case 4:
            return (unsigned int)((arg1 - 17));
        case 5:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) & 4095));
        case 6:
            return (((unsigned long)((unsigned int)(arg1)) & -0xff01LL) | ((((((unsigned long)((unsigned int)(arg1)) >> 8) & 255) | 2) & 255) << 8));
    }
}
adt204_switch_c pass 24 lines
// glaurung: adt204_switch_c @ 0x1270
int32_t adt204_switch_c(int32_t arg0, int32_t arg1) {
    long var2;
    var2 = (unsigned long)((unsigned int)((arg0 & 7)));
    if (((unsigned long)(6) < (unsigned long)((unsigned long)((unsigned char)(((var2 == 0) ? 7 : ((var2 == 1) ? 1 : ((var2 == 2) ? 5 : ((var2 == 3) ? 3 : ((var2 == 4) ? 0 : ((var2 == 5) ? 6 : ((var2 == 6) ? 2 : ((var2 == 7) ? 4 : *(char *)((0x2070 + var2))))))))))))))) {
        return (unsigned int)((2 - arg1));
    }
    switch ((unsigned int)((unsigned char)(((var2 == 0) ? 7 : ((var2 == 1) ? 1 : ((var2 == 2) ? 5 : ((var2 == 3) ? 3 : ((var2 == 4) ? 0 : ((var2 == 5) ? 6 : ((var2 == 6) ? 2 : ((var2 == 7) ? 4 : *(char *)((0x2070 + var2))))))))))))) {
        case 0:
            return (unsigned int)((arg1 + 21));
        case 1:
            return (unsigned int)((arg1 + (arg1 * 4)));
        case 2:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) ^ 0x4321));
        case 3:
            return (unsigned int)(((unsigned long)((unsigned int)(arg1)) >> 3));
        case 4:
            return (unsigned int)((arg1 - 27));
        case 5:
            return (unsigned int)((unsigned short)((arg1 & 0xffff)));
        case 6:
            return (((unsigned long)((unsigned int)(arg1)) & -0xff01LL) | ((((((unsigned long)((unsigned int)(arg1)) >> 8) & 255) | 4) & 255) << 8));
    }
}

← 213 fixtures