Fixture 90

bitfields

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

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

Bitfields: a plain int bitfield's signedness is implementation-defined, an explicitly signed 3-bit field holds -4..3, and reading one is a shift/mask pair whose exact widths must survive.

tests/decompiler_fixtures/src/90_bitfields.c source
#include <stdint.h>

/* Bitfields: a plain `int` bitfield's signedness is implementation-defined, an
 * explicitly signed 3-bit field holds -4..3, and reading one is a shift/mask
 * pair whose exact widths must survive. */

struct Flags {
    unsigned int low : 3;
    unsigned int middle : 5;
    signed int high : 4;
    unsigned int rest : 20;
};

__attribute__((noinline)) int32_t
bitfield_extract(uint32_t packed, int32_t which) {
    struct Flags flags;
    flags.low = packed & 0x7u;
    flags.middle = (packed >> 3) & 0x1Fu;
    flags.high = (int32_t)((packed >> 8) & 0xFu);
    flags.rest = (packed >> 12) & 0xFFFFFu;
    switch (which & 3) {
    case 0:
        return (int32_t)flags.low;
    case 1:
        return (int32_t)flags.middle;
    case 2:
        return (int32_t)flags.high; /* sign-extends from 4 bits */
    default:
        return (int32_t)flags.rest;
    }
}

__attribute__((noinline)) int32_t
bitfield_signed_range(int32_t value) {
    struct Flags flags;
    flags.low = 0;
    flags.middle = 0;
    flags.rest = 0;
    flags.high = value; /* truncates to 4 bits, then sign-extends on read */
    return flags.high;
}

__attribute__((noinline)) int32_t
bitfield_struct_size(void) {
    return (int32_t)sizeof(struct Flags);
}

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
bitfield_extract pass 26 lines
// glaurung: bitfield_extract @ 0x1100
__attribute__((no_stack_protector)) int32_t bitfield_extract(uint32_t arg0, int32_t arg1) {
    unsigned char local_10[4];
    int local_14;
    int var33;
    // x86-64 prologue: save rbp
    *(int *)(&local_10[0]) = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) & -8))) | (unsigned long)((unsigned int)((arg0 & 7))));
    *(int *)(&local_10[0]) = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) & -249))) | (unsigned long)((unsigned int)((arg0 & 248))));
    *(int *)(&local_10[0]) = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) & -3841))) | (unsigned long)((unsigned int)((arg0 & 3840))));
    *(int *)(&local_10[0]) = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) & 4095))) | (unsigned long)((unsigned int)((arg0 & -4096))));
    var33 = ((unsigned int)(arg1) & 3);
    local_14 = var33;
    if (((unsigned long)((unsigned int)(var33)) == 0)) {
        return (unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) & 7));
    } else {
        if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_14)) - 1))) == 0)) {
            return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) >> 3))) & 31));
        } else {
            if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_14)) - 2))) == 0)) {
                return (unsigned int)(((int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) << 20)) >> 28));
            } else {
                return (unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_10[0]))) >> 12));
            }
        }
    }
}
bitfield_signed_range pass 11 lines
// glaurung: bitfield_signed_range @ 0x11e0
__attribute__((no_stack_protector)) int32_t bitfield_signed_range(int32_t arg0) {
    unsigned char local_8[4];
    // x86-64 prologue: save rbp
    *(int *)(&local_8[0]) = ((unsigned long)((unsigned int)(*(int *)(&local_8[0]))) & 0xfffffff8);
    *(int *)(&local_8[0]) = ((unsigned long)((unsigned int)(*(int *)(&local_8[0]))) & 0xffffff07);
    *(int *)(&local_8[0]) = ((unsigned long)((unsigned int)(*(int *)(&local_8[0]))) & 4095);
    *(int *)(&local_8[0]) = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_8[0]))) & -3841))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 15))) << 8))));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((int)(((unsigned long)((unsigned int)(*(int *)(&local_8[0]))) << 20)) >> 28));
}
bitfield_struct_size pass 6 lines
// glaurung: bitfield_struct_size @ 0x1230
int32_t bitfield_struct_size(void) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return 4;
}

clang -O2

3/3
bitfield_extract pass 17 lines
// glaurung: bitfield_extract @ 0x1100
int32_t bitfield_extract(uint32_t arg0, int32_t arg1) {
    long var0;
    long var2;
    var0 = (unsigned long)(arg0);
    var2 = (unsigned long)((unsigned int)((arg1 & 3)));
    if (((unsigned long)((unsigned int)(var2)) == 2)) {
        return (unsigned int)(((int)((var0 << 20)) >> 28));
    }
    if (((unsigned long)((unsigned int)(var2)) == 1)) {
        return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var0)) >> 3))) & 31));
    }
    if (((unsigned long)((unsigned int)(var2)) != 0)) {
        return (unsigned int)(((unsigned long)((unsigned int)(var0)) >> 12));
    }
    return (unsigned int)(((unsigned long)(arg0) & 7));
}
bitfield_signed_range pass 4 lines
// glaurung: bitfield_signed_range @ 0x1130
int32_t bitfield_signed_range(int32_t arg0) {
    return (unsigned int)(((int)(((unsigned long)((unsigned int)(arg0)) << 28)) >> 28));
}
bitfield_struct_size pass 4 lines
// glaurung: bitfield_struct_size @ 0x1140
int32_t bitfield_struct_size(void) {
    return 4;
}

gcc -O0

3/3
bitfield_extract pass 34 lines
// glaurung: bitfield_extract @ 0x10f9
__attribute__((no_stack_protector)) int32_t bitfield_extract(uint32_t arg0, int32_t arg1) {
    unsigned char local_4[4];
    long var55;
    long var62;
    int var64;
    // x86-64 prologue: save rbp
    *(signed char *)(&local_4[0]) = ((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)(&local_4[0]))) & -8))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((arg0 & 7))) & 7))));
    *(signed char *)(&local_4[0]) = ((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)(&local_4[0]))) & 7))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned int)(arg0) >> 3))) & 31))) * 8))));
    *(signed char *)((&local_4[0] + 1)) = ((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((&local_4[0] + 1)))) & -16))) | (unsigned long)((unsigned int)((((signed char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned int)(arg0) >> 8))) << 4))) & 255)) >> 4) & 15))));
    *(int *)(&local_4[0]) = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_4[0]))) & 4095))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned int)(arg0) >> 12))) & 0xfffff))) << 12))));
    var55 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & 3)));
    if (((unsigned long)((unsigned int)(var55)) == 2)) {
        return (int)((signed char)((((signed char)(((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((&local_4[0] + 1)))) << 4))) & 255)) >> 4) & 255)));
    } else {
        if (((((unsigned long)((unsigned int)(var55)) == 2) | ((long)((int)(var55)) < 2)) == 0)) {
            var62 = (unsigned long)((unsigned int)(*(int *)(&local_4[0])));
            var64 = ((unsigned long)((unsigned int)(var62)) >> 12);
            // x86-64 epilogue: restore rbp
            return (unsigned int)(var64);
        }
        if (((unsigned long)((unsigned int)(var55)) == 0)) {
            return (unsigned int)((unsigned char)(((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)(&local_4[0]))) & 7))) & 255)));
        } else {
            if (((unsigned long)((unsigned int)(var55)) == 1)) {
                return (unsigned int)((unsigned char)((((unsigned long)((unsigned char)(((unsigned int)((unsigned char)(*(char *)(&local_4[0]))) & 255))) >> 3) & 255)));
            } else {
                var62 = (unsigned long)((unsigned int)(*(int *)(&local_4[0])));
                var64 = ((unsigned long)((unsigned int)(var62)) >> 12);
                return (unsigned int)(var64);
            }
        }
    }
}
bitfield_signed_range pass 11 lines
// glaurung: bitfield_signed_range @ 0x11be
__attribute__((no_stack_protector)) int32_t bitfield_signed_range(int32_t arg0) {
    unsigned char local_4[4];
    // x86-64 prologue: save rbp
    *(signed char *)(&local_4[0]) = ((unsigned int)((unsigned char)(*(char *)(&local_4[0]))) & 248);
    *(signed char *)(&local_4[0]) = ((unsigned int)((unsigned char)(*(char *)(&local_4[0]))) & 7);
    *(int *)(&local_4[0]) = ((unsigned long)((unsigned int)(*(int *)(&local_4[0]))) & 4095);
    *(signed char *)((&local_4[0] + 1)) = ((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((&local_4[0] + 1)))) & -16))) | (unsigned long)((unsigned int)((((signed char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) << 4))) & 255)) >> 4) & 15))));
    // x86-64 epilogue: restore rbp
    return (int)((signed char)((((signed char)(((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((&local_4[0] + 1)))) << 4))) & 255)) >> 4) & 255)));
}
bitfield_struct_size pass 6 lines
// glaurung: bitfield_struct_size @ 0x1211
int32_t bitfield_struct_size(void) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return 4;
}

gcc -O2

3/3
bitfield_extract pass 12 lines
// glaurung: bitfield_extract @ 0x1100
int32_t bitfield_extract(uint32_t arg0, int32_t arg1) {
    long var1;
    var1 = (unsigned long)((unsigned int)((arg1 & 3)));
    if (((unsigned long)((unsigned int)(var1)) == 1)) {
        return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) >> 3))) & 31));
    }
    if (((unsigned long)((unsigned int)(var1)) == 2)) {
        return (int)((signed char)((((signed char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) >> 8))) << 4))) & 255)) >> 4) & 255)));
    }
    return (((unsigned long)((unsigned int)(var1)) != 0) ? (unsigned long)((unsigned int)(((unsigned long)(arg0) >> 12))) : (unsigned long)((unsigned int)(((unsigned long)(arg0) & 7))));
}
bitfield_signed_range pass 4 lines
// glaurung: bitfield_signed_range @ 0x1140
int32_t bitfield_signed_range(int32_t arg0) {
    return (int)((signed char)((((signed char)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) << 4))) & 255)) >> 4) & 255)));
}
bitfield_struct_size pass 4 lines
// glaurung: bitfield_struct_size @ 0x1150
int32_t bitfield_struct_size(void) {
    return 4;
}

← 213 fixtures