Fixture 122

compound assignment

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

One lane has a function that returns a different result after decompilation: clang-O2 (2/3).

In E1 op= E2 the lvalue E1 is evaluated exactly once, which matters when the subscript itself has a side effect. Compound assignment also converts back to the left operand's type, so a narrow target truncates each step.

tests/decompiler_fixtures/src/122_compound_assignment.c source
#include <stdint.h>

/* In `E1 op= E2` the lvalue E1 is evaluated exactly once, which matters when
 * the subscript itself has a side effect. Compound assignment also converts
 * back to the left operand's type, so a narrow target truncates each step. */

__attribute__((noinline)) int32_t
subscript_evaluated_once(int32_t *values, int32_t count, int32_t *cursor) {
    if (values == 0 || cursor == 0 || count < 1 || count > 16) {
        return -1;
    }
    *cursor = 0;
    /* The postincrement happens once, not twice. */
    values[(*cursor)++] += 100;
    return *cursor;
}

__attribute__((noinline)) int32_t
narrow_compound_truncates(int32_t seed, int32_t steps) {
    uint8_t narrow = (uint8_t)seed;
    int32_t step;
    if (steps < 0 || steps > 16) {
        return -1;
    }
    for (step = 0; step < steps; ++step) {
        narrow += 50; /* wraps at 256 every time, not once at the end */
    }
    return (int32_t)narrow;
}

__attribute__((noinline)) int32_t
mixed_compound_operators(int32_t seed) {
    int32_t value = seed;
    value += 7;
    value *= 3;
    value ^= 0x55;
    value >>= 1;
    value |= 1;
    value -= 2;
    return value;
}

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 -O2

2/3
mixed_compound_operators pass 5 lines
// glaurung: mixed_compound_operators @ 0x1240
int32_t mixed_compound_operators(int32_t arg0) {
    int value;
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((int)(((unsigned long)((unsigned int)((arg0 + (arg0 * 2)))) + 21)) >> 1))) | 1))) ^ 42))) - 2));
}
narrow_compound_truncates fail 91 lines
// glaurung: narrow_compound_truncates @ 0x1130
int32_t narrow_compound_truncates(int32_t arg0, int32_t arg1) {
    extern long __unknown(long, ...);
    int step;
    long ret;
    long var0;
    long var2;
    int var3;
    int var30;
    int var34;
    long var39;
    int var40;
    long var5;
    int var6;
    int var62;
    int var72;
    int var79;
    long var86;
    long var87;
    int var89;
    int var91;
    ret = 0xffffffff;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return ret;
    }
    var0 = (unsigned long)((unsigned int)(arg0));
    if (((unsigned long)((unsigned int)(arg1)) == 0)) {
        return (unsigned int)((unsigned char)((var0 & 255)));
    }
    var2 = 0;
    var3 = arg0;
    if (((unsigned long)((unsigned long)((unsigned int)(arg1))) < (unsigned long)(8))) {
        goto L_1214;
    }
    var5 = 0;
    var6 = arg0;
    if (((unsigned long)(16) <= (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        var5 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & -16)));
        ret = ((unsigned long)((unsigned int)(arg0)) & 255);
        /* asm: packuswb */
        /* asm: packuswb */
        /* asm: paddb */
        /* asm: paddb */
        /* asm: psadbw */
        var6 = 0;
        var0 = 0;
        if (((unsigned int)(var5) == (unsigned int)(arg1))) {
            return (unsigned int)((unsigned char)((var0 & 255)));
        }
        var2 = var5;
        var3 = 0;
        if (((unsigned long)((unsigned char)((arg1 & 8))) == 0)) {
            goto L_1214;
        }
    }
    var2 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & -8)));
    var30 = var6;
    ret = 0;
    var34 = 0;
    var39 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var5)) - var2)));
    var40 = 255;
    do {
        /* asm: packuswb */
        /* asm: packuswb */
        var62 = (var30 & var40);
        /* asm: paddb */
        var72 = __unknown(0);
        /* asm: punpckhwd */
        var30 = ((unsigned int)((unsigned short)(var72)) | ((unsigned int)((unsigned short)(var34)) << 16));
        var79 = (var39 + 8);
        var39 = (unsigned long)((unsigned int)(var79));
    } while (((unsigned long)((unsigned int)(var79)) != 0));
    ret = 0;
    /* asm: psadbw */
    var3 = (unsigned int)((unsigned char)(((unsigned long)((unsigned int)(var62)) & 255)));
    var0 = (unsigned long)((unsigned int)(var3));
    if (((unsigned int)(var2) == (unsigned int)(arg1))) {
        return (unsigned int)((unsigned char)((var0 & 255)));
    }
    L_1214: ;
    var86 = (unsigned long)((unsigned int)((arg1 - var2)));
    var87 = (unsigned long)((unsigned int)(var3));
    do {
        var89 = ((unsigned int)((unsigned char)((var87 & 255))) + 50);
        var91 = (var86 - 1);
        var86 = (unsigned long)((unsigned int)(var91));
        var87 = (unsigned long)((unsigned int)(var89));
        var0 = (unsigned long)((unsigned int)(var89));
    } while (((unsigned long)((unsigned int)(var91)) != 0));
    return (unsigned int)((unsigned char)((var0 & 255)));
}
subscript_evaluated_once pass 16 lines
// glaurung: subscript_evaluated_once @ 0x1100
int32_t subscript_evaluated_once(int32_t * arg0, int32_t arg1, int32_t * arg2) {
    long ret;
    ret = 0xffffffff;
    if (((unsigned long)(0xfffffff0) <= (unsigned long)((unsigned long)((unsigned int)((arg1 - 17)))))) {
        if ((arg0 == 0)) {
            return ret;
        }
        if ((arg2 != 0)) {
            *(int *)(((long)arg2)) = 1;
            *(int *)(((long)arg0)) = (*(int *)(((long)arg0)) + 100);
            ret = (unsigned long)((unsigned int)(*(int *)(((long)arg2))));
        }
    }
    return ret;
}

clang -O0

3/3
mixed_compound_operators pass 14 lines
// glaurung: mixed_compound_operators @ 0x11f0
int32_t mixed_compound_operators(int32_t arg0) {
    int value;
    // x86-64 prologue: save rbp
    value = arg0;
    value = ((unsigned int)(value) + 7);
    value = (value * 3);
    value = ((unsigned int)(value) ^ 85);
    value = ((int)(value) >> 1);
    value = ((unsigned int)(value) | 1);
    value = ((unsigned int)(value) - 2);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(value);
}
narrow_compound_truncates pass 23 lines
// glaurung: narrow_compound_truncates @ 0x1180
int32_t narrow_compound_truncates(int32_t arg0, int32_t arg1) {
    unsigned char narrow;
    int step;
    int local_4;
    // x86-64 prologue: save rbp
    narrow = arg0;
    if (((long)(arg1) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    for (step = 0; (step < arg1); step++) {
        narrow = (narrow + 50);
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(narrow);
}
subscript_evaluated_once pass 33 lines
// glaurung: subscript_evaluated_once @ 0x1100
int32_t subscript_evaluated_once(int32_t * arg0, int32_t arg1, int32_t * arg2) {
    int local_4;
    long var3;
    // x86-64 prologue: save rbp
    if ((arg0 == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if ((arg2 == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((long)(arg1) < 1)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    *(int *)((long)arg2) = 0;
    var3 = (unsigned long)((unsigned int)(*(int *)((long)arg2)));
    *(int *)((long)arg2) = ((unsigned long)((unsigned int)(var3)) + 1);
    arg0[(long)((int)(var3))] = ((unsigned long)((unsigned int)(arg0[(long)((int)(var3))])) + 100);
    local_4 = *(int *)((long)arg2);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}

gcc -O0

3/3
mixed_compound_operators pass 14 lines
// glaurung: mixed_compound_operators @ 0x11be
int32_t mixed_compound_operators(int32_t arg0) {
    int value;
    // x86-64 prologue: save rbp
    value = arg0;
    value = (value + 7);
    value = ((unsigned int)(((unsigned long)((unsigned int)(value)) + (unsigned long)((unsigned int)(value)))) + (unsigned int)(value));
    value = (value ^ 85);
    value = ((int)(value) >> 1);
    value = (value | 1);
    value = (value - 2);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(value);
}
narrow_compound_truncates pass 20 lines
// glaurung: narrow_compound_truncates @ 0x1178
int32_t narrow_compound_truncates(int32_t arg0, int32_t arg1) {
    unsigned char narrow;
    int step;
    // x86-64 prologue: save rbp
    narrow = arg0;
    if (((long)(arg1) < 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    for (step = 0; (step < arg1); step++) {
        narrow = (narrow + 50);
    }
    // x86-64 epilogue: restore rbp
    return (unsigned int)(narrow);
}
subscript_evaluated_once pass 27 lines
// glaurung: subscript_evaluated_once @ 0x10f9
int32_t subscript_evaluated_once(int32_t * arg0, int32_t arg1, int32_t * arg2) {
    long var2;
    // x86-64 prologue: save rbp
    if ((arg0 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if ((arg2 == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if ((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0))) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        // x86-64 epilogue: restore rbp
        return 0xffffffff;
    }
    *(int *)((long)arg2) = 0;
    var2 = (unsigned long)((unsigned int)(*(int *)((long)arg2)));
    *(int *)((long)arg2) = (var2 + 1);
    arg0[(long)((int)(var2))] = ((unsigned long)((unsigned int)(arg0[(long)((int)(var2))])) + 100);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(*(int *)((long)arg2));
}

gcc -O2

3/3
mixed_compound_operators pass 5 lines
// glaurung: mixed_compound_operators @ 0x1160
int32_t mixed_compound_operators(int32_t arg0) {
    int value;
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((int)(((arg0 + (arg0 * 2)) + 21)) >> 1))) ^ 42))) | 1))) - 2));
}
narrow_compound_truncates pass 14 lines
// glaurung: narrow_compound_truncates @ 0x1130
int32_t narrow_compound_truncates(int32_t arg0, int32_t arg1) {
    unsigned char narrow;
    int step;
    long var0;
    var0 = (unsigned long)((unsigned int)(arg0));
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return 0xffffffff;
    }
    if (((unsigned long)((unsigned int)(arg1)) != 0)) {
        var0 = (unsigned long)((unsigned int)((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((arg1 - 1))) * 50))) + arg0) + 50)));
    }
    return (unsigned int)((unsigned char)((var0 & 255)));
}
subscript_evaluated_once pass 14 lines
// glaurung: subscript_evaluated_once @ 0x1100
int32_t subscript_evaluated_once(int32_t * arg0, int32_t arg1, int32_t * arg2) {
    if ((arg0 != 0)) {
        if ((arg2 == 0)) {
            return 0xffffffff;
        }
        if (((unsigned long)((unsigned long)((unsigned int)((arg1 - 1)))) <= (unsigned long)(15))) {
            *(int *)(((long)arg2)) = 1;
            *(int *)(((long)arg0)) = (*(int *)(((long)arg0)) + 100);
            return (unsigned int)(*(int *)(((long)arg2)));
        }
    }
    return 0xffffffff;
}

← 213 fixtures