Fixture 97

signed unsigned pitfalls

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

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

The classic conversion traps, written with explicit casts so the intent is unambiguous and the build stays warning-clean. Each returns a value that differs from the naive signed reading.

tests/decompiler_fixtures/src/97_signed_unsigned_pitfalls.c source
#include <stdint.h>

/* The classic conversion traps, written with explicit casts so the intent is
 * unambiguous and the build stays warning-clean. Each returns a value that
 * differs from the naive signed reading. */

__attribute__((noinline)) int32_t
negative_compares_greater(int32_t value, uint32_t bound) {
    /* -1 converted to unsigned is UINT32_MAX, so it is NOT less than bound. */
    return ((uint32_t)value < bound) ? 1 : 0;
}

__attribute__((noinline)) uint32_t
unsigned_subtraction_wraps(uint32_t left, uint32_t right) {
    /* Defined modular arithmetic: 3 - 5 is UINT32_MAX - 1. */
    return left - right;
}

__attribute__((noinline)) int32_t
size_like_loop(int32_t count) {
    uint32_t index;
    int32_t iterations = 0;
    if (count < 0 || count > 16) {
        return -1;
    }
    /* A reverse loop on an unsigned counter must not test `index >= 0`; this
     * one tests the post-decrement value instead. */
    for (index = (uint32_t)count; index-- > 0u;) {
        iterations += 1;
    }
    return iterations;
}

__attribute__((noinline)) int32_t
division_truncates_toward_zero(int32_t numerator, int32_t denominator) {
    if (denominator == 0 || (numerator == (-2147483647 - 1) && denominator == -1)) {
        return 0;
    }
    /* C99 requires truncation toward zero, so -7/2 is -3 and -7%2 is -1. */
    return (numerator / denominator) * 100 + (numerator % denominator);
}

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
division_truncates_toward_zero pass 21 lines
// glaurung: division_truncates_toward_zero @ 0x11b0
int32_t division_truncates_toward_zero(int32_t arg0, int32_t arg1) {
    int local_10;
    int local_4;
    if (((unsigned long)((unsigned int)(arg1)) != 0)) {
        if (((unsigned long)((unsigned int)(arg0)) != 0x80000000)) {
            goto L_11e7;
        }
        if (((unsigned long)((unsigned int)(arg1)) != 0xffffffff)) {
            goto L_11e7;
        }
    }
    local_4 = 0;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
    L_11e7: ;
    local_10 = (((int)((((long long)(int)((((unsigned long)((long)((int)((unsigned long)((unsigned int)(arg0))))) >> 32) & 0xffffffff)) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1))) * 100);
    local_4 = ((unsigned int)(local_10) + ((int)((((long long)(int)((((unsigned long)((long)((int)((unsigned long)((unsigned int)(arg0))))) >> 32) & 0xffffffff)) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1))));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
negative_compares_greater pass 6 lines
// glaurung: negative_compares_greater @ 0x1100
int32_t negative_compares_greater(int32_t arg0, uint32_t arg1) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)((unsigned long)(arg1))) ? 1 : 0);
}
size_like_loop pass 31 lines
// glaurung: size_like_loop @ 0x1140
int32_t size_like_loop(int32_t arg0) {
    int iterations;
    unsigned int index;
    int local_4;
    long var1;
    // x86-64 prologue: save rbp
    iterations = 0;
    if (((long)(arg0) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    index = arg0;
    while (1) {
        var1 = (unsigned long)(index);
        index = (index - 1);
        if (((unsigned long)((unsigned long)((unsigned int)(var1))) <= (unsigned long)(0))) {
            break;
        }
        iterations = ((unsigned int)(iterations) + 1);
    }
    local_4 = iterations;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
unsigned_subtraction_wraps pass 6 lines
// glaurung: unsigned_subtraction_wraps @ 0x1120
uint32_t unsigned_subtraction_wraps(uint32_t arg0, uint32_t arg1) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)(arg0) - arg1));
}

clang -O2

4/4
division_truncates_toward_zero pass 29 lines
// glaurung: division_truncates_toward_zero @ 0x1130
int32_t division_truncates_toward_zero(int32_t arg0, int32_t arg1) {
    long ret;
    long var2;
    int var3;
    int var4;
    long var5;
    int var6;
    ret = 0;
    if (((unsigned long)((unsigned int)(arg1)) != 0)) {
        if (((unsigned long)((unsigned int)(arg0)) != 0x80000000)) {
            var2 = (((unsigned long)((long)(arg0)) >> 32) & 0xffffffff);
            var3 = ((int)((((long long)(int)(var2) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1)));
            var4 = ((int)((((long long)(int)(var2) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1)));
            var5 = ((unsigned long)((unsigned int)(var3)) * 100);
            var6 = (var5 + var4);
            return (unsigned int)(var6);
        }
        if (((unsigned long)((unsigned int)(arg1)) != 0xffffffff)) {
            var2 = (((unsigned long)((long)(arg0)) >> 32) & 0xffffffff);
            var3 = ((int)((((long long)(int)(var2) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1)));
            var4 = ((int)((((long long)(int)(var2) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1)));
            var5 = ((unsigned long)((unsigned int)(var3)) * 100);
            var6 = (var5 + var4);
            return (unsigned int)(var6);
        }
    }
    return ret;
}
negative_compares_greater pass 4 lines
// glaurung: negative_compares_greater @ 0x1100
int32_t negative_compares_greater(int32_t arg0, uint32_t arg1) {
    return ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)((unsigned long)(arg1)));
}
size_like_loop pass 4 lines
// glaurung: size_like_loop @ 0x1120
int32_t size_like_loop(int32_t arg0) {
    return (((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)(17)) ? arg0 : 0xffffffff);
}
unsigned_subtraction_wraps pass 4 lines
// glaurung: unsigned_subtraction_wraps @ 0x1110
uint32_t unsigned_subtraction_wraps(uint32_t arg0, uint32_t arg1) {
    return (unsigned int)(((unsigned long)(arg0) - arg1));
}

gcc -O0

4/4
division_truncates_toward_zero pass 17 lines
// glaurung: division_truncates_toward_zero @ 0x116e
int32_t division_truncates_toward_zero(int32_t arg0, int32_t arg1) {
    if (((unsigned long)((unsigned int)(arg1)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((unsigned long)((unsigned int)(arg0)) != 0x80000000)) {
        // x86-64 epilogue: restore rbp
        return (unsigned int)(((unsigned long)((unsigned int)(((int)((((long long)(int)((((unsigned long)((long)((int)((unsigned long)((unsigned int)(arg0))))) >> 32) & 0xffffffff)) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1))))) + (((int)((((long long)(int)((((unsigned long)((long)((int)((unsigned long)((unsigned int)(arg0))))) >> 32) & 0xffffffff)) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1))) * 100)));
    }
    if (((unsigned long)((unsigned int)(arg1)) != 0xffffffff)) {
        // x86-64 epilogue: restore rbp
        return (unsigned int)(((unsigned long)((unsigned int)(((int)((((long long)(int)((((unsigned long)((long)((int)((unsigned long)((unsigned int)(arg0))))) >> 32) & 0xffffffff)) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1))))) + (((int)((((long long)(int)((((unsigned long)((long)((int)((unsigned long)((unsigned int)(arg0))))) >> 32) & 0xffffffff)) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1))) * 100)));
    }
    // x86-64 epilogue: restore rbp
    return 0;
}
negative_compares_greater pass 6 lines
// glaurung: negative_compares_greater @ 0x10f9
int32_t negative_compares_greater(int32_t arg0, uint32_t arg1) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)((unsigned long)(arg1)));
}
size_like_loop pass 27 lines
// glaurung: size_like_loop @ 0x112b
int32_t size_like_loop(int32_t arg0) {
    int iterations;
    unsigned int index;
    long var1;
    // x86-64 prologue: save rbp
    iterations = 0;
    if (((long)(arg0) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg0)) == 16) | ((long)(arg0) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    index = arg0;
    while (1) {
        var1 = (unsigned long)(index);
        index = (index - 1);
        if (((unsigned long)((unsigned int)(var1)) == 0)) {
            break;
        }
        iterations = (iterations + 1);
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(iterations);
}
unsigned_subtraction_wraps pass 6 lines
// glaurung: unsigned_subtraction_wraps @ 0x1115
uint32_t unsigned_subtraction_wraps(uint32_t arg0, uint32_t arg1) {
    // x86-64 prologue: save rbp
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)(arg0) - arg1));
}

gcc -O2

4/4
division_truncates_toward_zero pass 19 lines
// glaurung: division_truncates_toward_zero @ 0x1140
int32_t division_truncates_toward_zero(int32_t arg0, int32_t arg1) {
    long t70;
    long var1;
    var1 = 0;
    if (((unsigned long)((unsigned int)(arg1)) != 0)) {
        if (((unsigned long)((unsigned int)(arg0)) != 0x80000000)) {
            t70 = (((unsigned long)((long)(arg0)) >> 32) & 0xffffffff);
            var1 = (unsigned long)((unsigned int)(((((int)((((long long)(int)(t70) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1))) * 100) + ((int)((((long long)(int)(t70) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1))))));
            return (unsigned int)(var1);
        }
        if (((unsigned long)((unsigned int)(arg1)) == 0xffffffff)) {
            return (unsigned int)(var1);
        }
        t70 = (((unsigned long)((long)(arg0)) >> 32) & 0xffffffff);
        var1 = (unsigned long)((unsigned int)(((((int)((((long long)(int)(t70) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) / (int)(arg1))) * 100) + ((int)((((long long)(int)(t70) * (((long long)1) << 32)) + (unsigned int)((unsigned long)((unsigned int)(arg0)))) % (int)(arg1))))));
    }
    return (unsigned int)(var1);
}
negative_compares_greater pass 4 lines
// glaurung: negative_compares_greater @ 0x1100
int32_t negative_compares_greater(int32_t arg0, uint32_t arg1) {
    return ((unsigned long)((unsigned long)((unsigned int)(arg0))) < (unsigned long)((unsigned long)(arg1)));
}
size_like_loop pass 8 lines
// glaurung: size_like_loop @ 0x1120
int32_t size_like_loop(int32_t arg0) {
    int iterations;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
        return 0xffffffff;
    }
    return (unsigned int)(arg0);
}
unsigned_subtraction_wraps pass 4 lines
// glaurung: unsigned_subtraction_wraps @ 0x1110
uint32_t unsigned_subtraction_wraps(uint32_t arg0, uint32_t arg1) {
    return (unsigned int)(((unsigned long)(arg0) - arg1));
}

← 213 fixtures