Fixture 93

generic selection

C · 4 functions · 4 lanes · 16 of 16 function-lanes behave identically

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

C11 _Generic resolves at translation time, so the emitted code contains only the selected branch. The interesting recovery question is that three source spellings collapse to three different widths of the same operation.

tests/decompiler_fixtures/src/93_generic_selection.c source
#include <stdint.h>

/* C11 _Generic resolves at translation time, so the emitted code contains only
 * the selected branch. The interesting recovery question is that three source
 * spellings collapse to three different widths of the same operation. */

#define WIDEN(v) _Generic((v), \
    int8_t: (int32_t)1, \
    int16_t: (int32_t)2, \
    int32_t: (int32_t)4, \
    int64_t: (int32_t)8, \
    default: (int32_t)0)

__attribute__((noinline)) int32_t generic_tag_of_int32(int32_t value) {
    return WIDEN(value) + (value != 0);
}

__attribute__((noinline)) int32_t generic_tag_of_int16(int32_t value) {
    int16_t narrow = (int16_t)value;
    return WIDEN(narrow) + (narrow != 0);
}

__attribute__((noinline)) int32_t generic_tag_of_int64(int32_t value) {
    int64_t wide = (int64_t)value;
    return WIDEN(wide) + (wide != 0);
}

__attribute__((noinline)) int32_t generic_dispatch(int32_t selector) {
    switch (selector & 3) {
    case 0:
        return generic_tag_of_int16(selector);
    case 1:
        return generic_tag_of_int32(selector);
    default:
        return generic_tag_of_int64(selector);
    }
}

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

4/4
generic_dispatch pass 26 lines
// glaurung: generic_dispatch @ 0x11b0
int32_t generic_dispatch(int32_t arg0) {
    extern int generic_tag_of_int16(int);
    extern int generic_tag_of_int32(int);
    extern int generic_tag_of_int64(int);
    int local_c;
    int var1;
    int var10;
    int var3;
    int var8;
    // x86-64 prologue: save rbp, frame 16 bytes
    var1 = ((unsigned int)(arg0) & 3);
    local_c = var1;
    if (((unsigned long)((unsigned int)(var1)) == 0)) {
        var3 = generic_tag_of_int16((unsigned long)((unsigned int)(arg0)));
        return (unsigned int)(var3);
    } else {
        if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_c)) - 1))) == 0)) {
            var8 = generic_tag_of_int32((unsigned long)((unsigned int)(arg0)));
            return (unsigned int)(var8);
        } else {
            var10 = generic_tag_of_int64((unsigned long)((unsigned int)(arg0)));
            return (unsigned int)(var10);
        }
    }
}
generic_tag_of_int16 pass 8 lines
// glaurung: generic_tag_of_int16 @ 0x1150
int32_t generic_tag_of_int16(int32_t arg0) {
    short narrow;
    // x86-64 prologue: save rbp
    narrow = arg0;
    // x86-64 epilogue: restore rbp
    return (unsigned int)((((unsigned long)((unsigned int)(narrow)) != 0) + 2));
}
generic_tag_of_int32 pass 6 lines
// glaurung: generic_tag_of_int32 @ 0x1130
int32_t generic_tag_of_int32(int32_t arg0) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)((((unsigned long)((unsigned int)(arg0)) != 0) + 4));
}
generic_tag_of_int64 pass 7 lines
// glaurung: generic_tag_of_int64 @ 0x1180
int32_t generic_tag_of_int64(int32_t arg0) {
    long wide;
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((arg0 != 0) + 8));
}

clang -O2

4/4
generic_dispatch pass 19 lines
// glaurung: generic_dispatch @ 0x1160
int32_t generic_dispatch(int32_t arg0) {
    extern int generic_tag_of_int16(int);
    extern int generic_tag_of_int32(int);
    extern int generic_tag_of_int64(int);
    int ret;
    long var2;
    var2 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3)));
    if (((unsigned long)((unsigned int)(var2)) == 1)) {
        ret = generic_tag_of_int32(arg0);
        return ret;
    }
    if (((unsigned long)((unsigned int)(var2)) != 0)) {
        ret = generic_tag_of_int64(arg0);
        return ret;
    }
    ret = generic_tag_of_int16(arg0);
    return ret;
}
generic_tag_of_int16 pass 4 lines
// glaurung: generic_tag_of_int16 @ 0x1140
int32_t generic_tag_of_int16(int32_t arg0) {
    return (unsigned int)((3 - ((unsigned long)((unsigned long)((unsigned short)((arg0 & 0xffff)))) < (unsigned long)(1))));
}
generic_tag_of_int32 pass 4 lines
// glaurung: generic_tag_of_int32 @ 0x1130
int32_t generic_tag_of_int32(int32_t arg0) {
    return (unsigned int)((5 - ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)(1))));
}
generic_tag_of_int64 pass 4 lines
// glaurung: generic_tag_of_int64 @ 0x1150
int32_t generic_tag_of_int64(int32_t arg0) {
    return (unsigned int)((9 - ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)(1))));
}

gcc -O0

4/4
generic_dispatch pass 24 lines
// glaurung: generic_dispatch @ 0x11c8
int32_t generic_dispatch(int32_t arg0) {
    extern int generic_tag_of_int16(int);
    extern int generic_tag_of_int32(int);
    extern int generic_tag_of_int64(int);
    long var2;
    int var4;
    int var6;
    int var8;
    // x86-64 prologue: save rbp, frame 16 bytes
    var2 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3)));
    if (((unsigned long)((unsigned int)(var2)) == 0)) {
        var4 = generic_tag_of_int16((unsigned long)((unsigned int)(arg0)));
        return var4;
    } else {
        if (((unsigned long)((unsigned int)(var2)) == 1)) {
            var6 = generic_tag_of_int32((unsigned long)((unsigned int)(arg0)));
            return var6;
        } else {
            var8 = generic_tag_of_int64((unsigned long)((unsigned int)(arg0)));
            return var8;
        }
    }
}
generic_tag_of_int16 pass 11 lines
// glaurung: generic_tag_of_int16 @ 0x1178
int32_t generic_tag_of_int16(int32_t arg0) {
    short narrow;
    // x86-64 prologue: save rbp
    narrow = arg0;
    if (((unsigned long)((unsigned short)(narrow)) == 0)) {
        return 2;
    } else {
        return 3;
    }
}
generic_tag_of_int32 pass 9 lines
// glaurung: generic_tag_of_int32 @ 0x1159
int32_t generic_tag_of_int32(int32_t arg0) {
    // x86-64 prologue: save rbp
    if (((unsigned long)((unsigned int)(arg0)) == 0)) {
        return 4;
    } else {
        return 5;
    }
}
generic_tag_of_int64 pass 11 lines
// glaurung: generic_tag_of_int64 @ 0x119f
int32_t generic_tag_of_int64(int32_t arg0) {
    long wide;
    // x86-64 prologue: save rbp
    wide = arg0;
    if ((wide == 0)) {
        return 8;
    } else {
        return 9;
    }
}

gcc -O2

4/4
generic_dispatch pass 21 lines
// glaurung: generic_dispatch @ 0x11a0
int32_t generic_dispatch(int32_t arg0) {
    extern int generic_tag_of_int16(int);
    extern int generic_tag_of_int32(int);
    extern int generic_tag_of_int64(int);
    int ret;
    int var1;
    long var2;
    var1 = ((unsigned int)(arg0) & 3);
    var2 = (unsigned long)((unsigned int)(var1));
    if (((unsigned long)((unsigned int)(var1)) == 0)) {
        ret = generic_tag_of_int16(arg0);
        return ret;
    }
    if (((unsigned long)((unsigned int)(var2)) == 1)) {
        ret = generic_tag_of_int32(arg0);
        return ret;
    }
    ret = generic_tag_of_int64(arg0);
    return ret;
}
generic_tag_of_int16 pass 4 lines
// glaurung: generic_tag_of_int16 @ 0x1170
int32_t generic_tag_of_int16(int32_t arg0) {
    return (unsigned int)((3 - ((unsigned long)((unsigned long)((unsigned short)((arg0 & 0xffff)))) < (unsigned long)(1))));
}
generic_tag_of_int32 pass 4 lines
// glaurung: generic_tag_of_int32 @ 0x1160
int32_t generic_tag_of_int32(int32_t arg0) {
    return (unsigned int)((5 - ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)(1))));
}
generic_tag_of_int64 pass 4 lines
// glaurung: generic_tag_of_int64 @ 0x1190
int32_t generic_tag_of_int64(int32_t arg0) {
    return (unsigned int)((9 - ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)(1))));
}

← 213 fixtures