Fixture 99

char and endianness

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

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

Plain char has implementation-defined signedness, so the same source reads 0x80 as -128 or 128 depending on target. Both spellings are pinned here, and the byte order is observed rather than assumed.

tests/decompiler_fixtures/src/99_char_and_endianness.c source
#include <stdint.h>

/* Plain `char` has implementation-defined signedness, so the same source reads
 * 0x80 as -128 or 128 depending on target. Both spellings are pinned here, and
 * the byte order is observed rather than assumed. */

__attribute__((noinline)) int32_t
plain_char_is_signed(void) {
    char probe = (char)0x80;
    return probe < 0;
}

__attribute__((noinline)) int32_t
char_widening(int32_t value) {
    signed char narrow = (signed char)value;
    unsigned char wide = (unsigned char)value;
    return (int32_t)narrow * 1000 + (int32_t)wide;
}

__attribute__((noinline)) uint32_t
byte_swap32(uint32_t value) {
    return ((value & 0x000000FFu) << 24) | ((value & 0x0000FF00u) << 8) |
           ((value & 0x00FF0000u) >> 8) | ((value & 0xFF000000u) >> 24);
}

__attribute__((noinline)) uint32_t
load_big_endian(const uint8_t *bytes, int32_t available) {
    if (bytes == 0 || available < 4) {
        return 0;
    }
    return ((uint32_t)bytes[0] << 24) | ((uint32_t)bytes[1] << 16) |
           ((uint32_t)bytes[2] << 8) | (uint32_t)bytes[3];
}

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
byte_swap32 pass 6 lines
// glaurung: byte_swap32 @ 0x1150
uint32_t byte_swap32(uint32_t arg0) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) & 255))) << 24))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) & 0xff00))) << 8)))))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) & 0xff0000))) >> 8)))))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) & -0x1000000LL))) >> 24)))));
}
char_widening pass 10 lines
// glaurung: char_widening @ 0x1120
int32_t char_widening(int32_t arg0) {
    signed char narrow;
    unsigned char wide;
    // x86-64 prologue: save rbp
    narrow = arg0;
    wide = arg0;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((narrow * 1000) + (unsigned int)(wide)));
}
load_big_endian pass 16 lines
// glaurung: load_big_endian @ 0x1190
uint32_t load_big_endian(const uint8_t * arg0, int32_t arg1) {
    int local_4;
    // x86-64 prologue: save rbp
    if ((arg0 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((long)(arg1) < 4)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    local_4 = ((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)((long)arg0))) << 24))) | (unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)(((long)arg0 + 0x1)))) << 16)))))) | (unsigned long)((unsigned int)(((unsigned int)((unsigned char)(*(char *)(((long)arg0 + 0x2)))) << 8))))) | (unsigned int)((unsigned char)(*(char *)(((long)arg0 + 0x3)))));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
plain_char_is_signed pass 8 lines
// glaurung: plain_char_is_signed @ 0x1100
int32_t plain_char_is_signed(void) {
    signed char probe;
    // x86-64 prologue: save rbp
    probe = -128;
    // x86-64 epilogue: restore rbp
    return ((long)(probe) < 0);
}

clang -O2

4/4
byte_swap32 pass 4 lines
// glaurung: byte_swap32 @ 0x1120
uint32_t byte_swap32(uint32_t arg0) {
    return (unsigned int)((((((arg0 & 255) << 24) | (((arg0 >> 8) & 255) << 16)) | (((arg0 >> 16) & 255) << 8)) | ((arg0 >> 24) & 255)));
}
char_widening pass 6 lines
// glaurung: char_widening @ 0x1110
int32_t char_widening(int32_t arg0) {
    int var0;
    var0 = (int)((signed char)((arg0 & 255)));
    return (unsigned int)(((unsigned int)((unsigned char)((var0 & 255))) + (var0 * 1000)));
}
load_big_endian pass 14 lines
// glaurung: load_big_endian @ 0x1130
uint32_t load_big_endian(const uint8_t * arg0, int32_t arg1) {
    long ret;
    long var2;
    ret = 0;
    if ((arg0 != 0)) {
        ret = 0;
        if ((4 <= (long)(arg1))) {
            var2 = (unsigned long)((unsigned int)(*(int *)(((long)arg0))));
            ret = (unsigned int)(((((((unsigned int)(var2) & 255) << 24) | ((((unsigned int)(var2) >> 8) & 255) << 16)) | ((((unsigned int)(var2) >> 16) & 255) << 8)) | (((unsigned int)(var2) >> 24) & 255)));
        }
    }
    return ret;
}
plain_char_is_signed pass 4 lines
// glaurung: plain_char_is_signed @ 0x1100
int32_t plain_char_is_signed(void) {
    return 1;
}

gcc -O0

4/4
byte_swap32 pass 6 lines
// glaurung: byte_swap32 @ 0x113a
uint32_t byte_swap32(uint32_t arg0) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) >> 24))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) << 24))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) << 8))) & 0xff0000)))))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)(arg0) >> 8))) & 0xff00))))))));
}
char_widening pass 10 lines
// glaurung: char_widening @ 0x1111
int32_t char_widening(int32_t arg0) {
    signed char narrow;
    unsigned char wide;
    // x86-64 prologue: save rbp
    narrow = arg0;
    wide = arg0;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned int)(wide) + (narrow * 1000)));
}
load_big_endian pass 14 lines
// glaurung: load_big_endian @ 0x1171
uint32_t load_big_endian(const uint8_t * arg0, int32_t arg1) {
    // x86-64 prologue: save rbp
    if ((arg0 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 3) | ((long)(arg1) < 3)) != 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(arg0[3])) & 255))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(*(char *)((long)arg0))) & 255))) << 24))) | (unsigned long)((unsigned int)(((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(arg0[1])) & 255))) << 16)))))) | (unsigned long)((unsigned int)(((unsigned int)((unsigned char)(((unsigned int)((unsigned char)(arg0[2])) & 255))) << 8))))))));
}
plain_char_is_signed pass 8 lines
// glaurung: plain_char_is_signed @ 0x10f9
int32_t plain_char_is_signed(void) {
    signed char probe;
    // x86-64 prologue: save rbp
    probe = -128;
    // x86-64 epilogue: restore rbp
    return (unsigned int)((unsigned char)((((unsigned long)((unsigned char)(((unsigned int)((unsigned char)(probe)) & 255))) >> 7) & 255)));
}

gcc -O2

4/4
byte_swap32 pass 4 lines
// glaurung: byte_swap32 @ 0x1130
uint32_t byte_swap32(uint32_t arg0) {
    return (unsigned int)((((((arg0 & 255) << 24) | (((arg0 >> 8) & 255) << 16)) | (((arg0 >> 16) & 255) << 8)) | ((arg0 >> 24) & 255)));
}
char_widening pass 6 lines
// glaurung: char_widening @ 0x1110
int32_t char_widening(int32_t arg0) {
    signed char narrow;
    unsigned char wide;
    return (unsigned int)((((int)((signed char)((arg0 & 255))) * 1000) + (unsigned int)((unsigned char)((arg0 & 255)))));
}
load_big_endian pass 11 lines
// glaurung: load_big_endian @ 0x1140
uint32_t load_big_endian(const uint8_t * arg0, int32_t arg1) {
    long var1;
    if ((arg0 != 0)) {
        if (((((unsigned long)((unsigned int)(arg1)) == 3) | ((long)(arg1) < 3)) == 0)) {
            var1 = (unsigned long)((unsigned int)(*(int *)(((long)arg0))));
            return (unsigned int)(((((((unsigned int)(var1) & 255) << 24) | ((((unsigned int)(var1) >> 8) & 255) << 16)) | ((((unsigned int)(var1) >> 16) & 255) << 8)) | (((unsigned int)(var1) >> 24) & 255)));
        }
    }
    return 0;
}
plain_char_is_signed pass 4 lines
// glaurung: plain_char_is_signed @ 0x1100
int32_t plain_char_is_signed(void) {
    return 1;
}

← 213 fixtures