Fixture 89

bool semantics

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

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

_Bool normalizes any nonzero value to 1 on conversion, which is a real instruction sequence (test/setne), not a no-op cast. Storing into a _Bool and reading it back is therefore lossy in a specific, observable way.

tests/decompiler_fixtures/src/89_bool_semantics.c source
#include <stdint.h>

/* _Bool normalizes any nonzero value to 1 on conversion, which is a real
 * instruction sequence (test/setne), not a no-op cast. Storing into a _Bool and
 * reading it back is therefore lossy in a specific, observable way. */

__attribute__((noinline)) int32_t
bool_normalizes(int32_t value) {
    _Bool flag = (_Bool)value;
    return (int32_t)flag;
}

__attribute__((noinline)) int32_t
bool_roundtrip_mask(int32_t value) {
    /* Only bit 8 is inspected, but the result is still normalized to 0 or 1. */
    _Bool flag = (value & 0x100) != 0;
    return flag ? 1000 : 2000;
}

__attribute__((noinline)) int32_t
bool_arithmetic(int32_t a, int32_t b, int32_t c) {
    /* Relational operators yield int 0/1; summing them counts satisfied
     * predicates without any branch. */
    return (a > b) + (b > c) + (c > a) + (_Bool)(a | b | c);
}

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
bool_arithmetic pass 6 lines
// glaurung: bool_arithmetic @ 0x1150
int32_t bool_arithmetic(int32_t arg0, int32_t arg1, int32_t arg2) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((((((unsigned int)(arg0) == (unsigned int)(arg1)) | (arg0 < arg1)) == 0) + ((((unsigned int)(arg1) == (unsigned int)(arg2)) | (arg1 < arg2)) == 0)))) + ((((unsigned int)(arg2) == (unsigned int)(arg0)) | (arg2 < arg0)) == 0)))) + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) | arg1))) | arg2))) != 0)));
}
bool_normalizes pass 7 lines
// glaurung: bool_normalizes @ 0x1100
int32_t bool_normalizes(int32_t arg0) {
    int flag;
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return ((unsigned long)((unsigned int)(arg0)) != 0);
}
bool_roundtrip_mask pass 7 lines
// glaurung: bool_roundtrip_mask @ 0x1120
int32_t bool_roundtrip_mask(int32_t arg0) {
    int flag;
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 256))) != 0) ? 1000 : 2000);
}

clang -O2

3/3
bool_arithmetic pass 4 lines
// glaurung: bool_arithmetic @ 0x1130
int32_t bool_arithmetic(int32_t arg0, int32_t arg1, int32_t arg2) {
    return (unsigned int)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((arg1 | arg0))) | arg2))) != 0) + (unsigned long)((unsigned int)((((((unsigned int)(arg2) == (unsigned int)(arg0)) | (arg2 < arg0)) == 0) + (unsigned long)((unsigned int)((((((unsigned int)(arg1) == (unsigned int)(arg2)) | (arg1 < arg2)) == 0) + ((((unsigned int)(arg0) == (unsigned int)(arg1)) | (arg0 < arg1)) == 0)))))))));
}
bool_normalizes pass 4 lines
// glaurung: bool_normalizes @ 0x1100
int32_t bool_normalizes(int32_t arg0) {
    return ((unsigned long)((unsigned int)(arg0)) != 0);
}
bool_roundtrip_mask pass 4 lines
// glaurung: bool_roundtrip_mask @ 0x1110
int32_t bool_roundtrip_mask(int32_t arg0) {
    return (((unsigned long)((unsigned int)((arg0 & 256))) == 0) ? 2000 : 1000);
}

gcc -O0

3/3
bool_arithmetic pass 6 lines
// glaurung: bool_arithmetic @ 0x1143
int32_t bool_arithmetic(int32_t arg0, int32_t arg1, int32_t arg2) {
    // 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)(arg0)) | arg1))) | arg2))) != 0) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((((((unsigned int)(arg0) == (unsigned int)(arg1)) | (arg0 < arg1)) == 0) + ((((unsigned int)(arg1) == (unsigned int)(arg2)) | (arg1 < arg2)) == 0)))) + ((((unsigned int)(arg2) == (unsigned int)(arg0)) | (arg2 < arg0)) == 0))))));
}
bool_normalizes pass 7 lines
// glaurung: bool_normalizes @ 0x10f9
int32_t bool_normalizes(int32_t arg0) {
    int flag;
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return ((unsigned long)((unsigned int)(arg0)) != 0);
}
bool_roundtrip_mask pass 11 lines
// glaurung: bool_roundtrip_mask @ 0x1114
int32_t bool_roundtrip_mask(int32_t arg0) {
    int flag;
    // x86-64 prologue: save rbp
    flag = ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 256))) != 0);
    if (((unsigned long)((unsigned char)(flag)) == 0)) {
        return 2000;
    } else {
        return 1000;
    }
}

gcc -O2

3/3
bool_arithmetic pass 4 lines
// glaurung: bool_arithmetic @ 0x1130
int32_t bool_arithmetic(int32_t arg0, int32_t arg1, int32_t arg2) {
    return (unsigned int)(((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((((((unsigned int)(arg0) == (unsigned int)(arg1)) | (arg0 < arg1)) == 0) + ((((unsigned int)(arg1) == (unsigned int)(arg2)) | (arg1 < arg2)) == 0)))) + (arg0 < arg2)))) + 1)) - ((unsigned long)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((arg1 | arg2))) | arg0)))) < (unsigned long)(1))));
}
bool_normalizes pass 4 lines
// glaurung: bool_normalizes @ 0x1100
int32_t bool_normalizes(int32_t arg0) {
    return ((unsigned long)((unsigned int)(arg0)) != 0);
}
bool_roundtrip_mask pass 4 lines
// glaurung: bool_roundtrip_mask @ 0x1110
int32_t bool_roundtrip_mask(int32_t arg0) {
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned int)((0 - ((unsigned long)((unsigned long)((unsigned int)((arg0 & 256)))) < (unsigned long)(1)))) & 1000))) + 1000));
}

← 213 fixtures