Fixture 141

atomics

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

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

C11 atomics lower to lock-prefixed read-modify-write instructions and compare-exchange retry loops. The memory order argument constrains reordering without computing anything, so it is invisible in the data flow and visible only in which barriers are emitted.

tests/decompiler_fixtures/src/141_atomics.c source
#include <stdint.h>
#include <stdatomic.h>

/* C11 atomics lower to lock-prefixed read-modify-write instructions and
 * compare-exchange retry loops. The memory order argument constrains
 * reordering without computing anything, so it is invisible in the data flow
 * and visible only in which barriers are emitted. */

__attribute__((noinline)) int32_t atomic_increment(int32_t start, int32_t times) {
    atomic_int cell;
    int32_t index;
    if (times < 0 || times > 16) {
        return -1;
    }
    atomic_init(&cell, start);
    for (index = 0; index < times; ++index) {
        atomic_fetch_add_explicit(&cell, 1, memory_order_relaxed);
    }
    return atomic_load_explicit(&cell, memory_order_acquire);
}

__attribute__((noinline)) int32_t
atomic_compare_exchange_loop(int32_t start, int32_t target) {
    atomic_int cell;
    int32_t expected = start;
    int32_t attempts = 0;
    atomic_init(&cell, start);
    /* The weak form may fail spuriously, so the retry edge is real control
     * flow that must survive. */
    while (!atomic_compare_exchange_weak_explicit(
               &cell, &expected, target, memory_order_acq_rel,
               memory_order_relaxed) &&
           attempts < 64) {
        attempts += 1;
    }
    return atomic_load_explicit(&cell, memory_order_seq_cst) * 10 + attempts;
}

__attribute__((noinline)) int32_t atomic_exchange_and_or(int32_t seed) {
    atomic_int cell;
    int32_t previous;
    atomic_init(&cell, seed);
    previous = atomic_exchange_explicit(&cell, seed ^ 0x5A5A, memory_order_acq_rel);
    atomic_fetch_or_explicit(&cell, 1, memory_order_release);
    return previous ^ atomic_load_explicit(&cell, memory_order_acquire);
}

__attribute__((noinline)) int32_t atomic_flag_round_trip(int32_t seed) {
    atomic_flag flag = ATOMIC_FLAG_INIT;
    int32_t first = atomic_flag_test_and_set_explicit(&flag, memory_order_acquire);
    int32_t second = atomic_flag_test_and_set_explicit(&flag, memory_order_acquire);
    atomic_flag_clear_explicit(&flag, memory_order_release);
    return (first ? 1 : 0) * 10 + (second ? 1 : 0) + (seed & 0);
}

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.

gcc -O2

3/4
atomic_compare_exchange_loop pass 55 lines
// glaurung: atomic_compare_exchange_loop @ 0x1180
int32_t atomic_compare_exchange_loop(int32_t arg0, int32_t arg1) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int expected;
    int attempts;
    long local_10;
    int local_14;
    long ret;
    long t0;
    long t141;
    long var0;
    long var3;
    long var6;
    long var9;
    long zf_10;
    long zf_3;
    var0 = (unsigned long)((unsigned int)(arg0));
    local_10 = (long)(0x28);
    var3 = 0;
    local_14 = arg0;
    t0 = (unsigned long)((unsigned int)(local_14));
    zf_3 = ((unsigned int)(arg0) == (unsigned int)(local_14));
    if (((unsigned int)(arg0) == (unsigned int)(local_14))) {
        local_14 = arg1;
    }
    expected = ((zf_3 == 0) ? t0 : var0);
    var6 = var3;
    if ((zf_3 == 0)) {
        while (1) {
            attempts = (unsigned long)((unsigned int)((var6 + 1)));
            t141 = (unsigned long)((unsigned int)(local_14));
            zf_10 = ((unsigned int)(expected) == (unsigned int)(local_14));
            if (((unsigned int)(expected) == (unsigned int)(local_14))) {
                local_14 = arg1;
            }
            expected = ((zf_10 == 0) ? t141 : expected);
            var9 = (unsigned long)((unsigned int)(attempts));
            if ((zf_10 != 0)) {
                break;
            }
            var6 = (unsigned long)((unsigned int)(attempts));
            if (((unsigned long)((unsigned int)(attempts)) == 64)) {
                var9 = (unsigned long)((unsigned int)(attempts));
                break;
            }
        }
    } else {
        var9 = 0;
    }
    ret = (unsigned long)((unsigned int)((var9 + ((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_14)) + ((unsigned long)((unsigned int)(local_14)) * 4)))) * 2))));
    if ((local_10 != 0x28)) {
        __stack_chk_fail();
    }
    return ret;
}
atomic_exchange_and_or pass 22 lines
// glaurung: atomic_exchange_and_or @ 0x11e0
int32_t atomic_exchange_and_or(int32_t arg0) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int __atomic_exchange_tmp;
    int __atomic_exchange_val;
    int __atomic_load_tmp;
    int __atomic_store_tmp;
    long local_10;
    int local_14;
    long ret;
    long t0;
    local_10 = (long)(0x28);
    local_14 = arg0;
    t0 = (unsigned long)((unsigned int)(local_14));
    local_14 = (arg0 ^ 0x5a5a);
    local_14 = (local_14 | 1);
    ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_14)) ^ t0)));
    if ((local_10 != 0x28)) {
        __stack_chk_fail();
    }
    return ret;
}
atomic_flag_round_trip fail 23 lines
// glaurung: atomic_flag_round_trip @ 0x1230
int32_t atomic_flag_round_trip(int32_t arg0) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int second;
    long local_10;
    unsigned char local_11[1];
    long ret;
    long t131;
    long var4;
    local_10 = (long)(0x28);
    ret = 1;
    *(signed char *)(&local_11[0]) = 0;
    *(signed char *)(&local_11[0]) = var4;
    t131 = *(char *)(&local_11[0]);
    *(signed char *)(&local_11[0]) = 1;
    ret = t131;
    *(signed char *)(&local_11[0]) = 0;
    ret = (unsigned long)((unsigned int)(((unsigned int)((unsigned char)((t131 & 255))) + 10)));
    if ((local_10 != 0x28)) {
        __stack_chk_fail();
    }
    return ret;
}
atomic_increment pass 28 lines
// glaurung: atomic_increment @ 0x1120
int32_t atomic_increment(int32_t arg0, int32_t arg1) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int index;
    long local_10;
    long ret;
    long var2;
    int var4;
    local_10 = (long)(0x28);
    var2 = 0;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        ret = 0xffffffff;
    } else {
        if (((unsigned long)((unsigned int)(arg1)) != 0)) {
            index = var2;
            do {
                arg0 = (arg0 + 1);
                var4 = (index + 1);
                index = (unsigned long)((unsigned int)(var4));
            } while (((unsigned int)(arg1) != (unsigned int)(var4)));
        }
        ret = (unsigned long)((unsigned int)(arg0));
    }
    if ((local_10 != 0x28)) {
        __stack_chk_fail();
    }
    return ret;
}

clang -O0

4/4
atomic_compare_exchange_loop pass 45 lines
// glaurung: atomic_compare_exchange_loop @ 0x1180
int32_t atomic_compare_exchange_loop(int32_t arg0, int32_t arg1) {
    int expected;
    int attempts;
    signed char local_19;
    int local_20;
    int local_24;
    signed char local_25;
    signed char local_26;
    int local_c;
    long t140;
    long var3;
    long var4;
    long var8;
    long zf_2;
    expected = arg0;
    attempts = 0;
    local_c = arg0;
    L_119d: ;
    var3 = (unsigned long)((unsigned int)(expected));
    var4 = (unsigned long)((unsigned int)(arg1));
    t140 = (unsigned long)((unsigned int)(local_c));
    zf_2 = ((unsigned int)(expected) == (unsigned int)(local_c));
    if (((unsigned int)(expected) == (unsigned int)(local_c))) {
        local_c = var4;
    }
    var8 = (zf_2 & 255);
    local_25 = var8;
    local_24 = ((zf_2 == 0) ? t140 : var3);
    if (((unsigned long)((unsigned char)((var8 & 1))) == 0)) {
        expected = local_24;
    }
    local_19 = (local_25 & 1);
    local_26 = 0;
    if (((unsigned long)((unsigned char)((local_19 & 1))) == 0)) {
        local_26 = ((long)(attempts) < 64);
    }
    if (((unsigned long)((unsigned char)((local_26 & 1))) != 0)) {
        attempts = ((unsigned int)(attempts) + 1);
        goto L_119d;
    }
    local_20 = local_c;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((local_20 * 10) + attempts));
}
atomic_exchange_and_or pass 46 lines
// glaurung: atomic_exchange_and_or @ 0x1220
int32_t atomic_exchange_and_or(int32_t arg0) {
    int previous;
    int local_10;
    int local_14;
    int local_1c;
    int local_20;
    int local_24;
    int local_28;
    int local_2c;
    int local_8;
    long t137;
    long t143;
    long var13;
    int var14;
    long var9;
    long zf_4;
    // x86-64 prologue: save rbp
    local_8 = arg0;
    local_10 = ((unsigned int)(arg0) ^ 0x5a5a);
    t137 = (unsigned long)((unsigned int)(local_8));
    local_8 = local_10;
    local_14 = t137;
    previous = local_14;
    local_28 = 1;
    local_24 = local_8;
    while (1) {
        var9 = (unsigned long)((unsigned int)(local_24));
        var13 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_24)) | (unsigned long)((unsigned int)(local_28)))));
        t143 = (unsigned long)((unsigned int)(local_8));
        zf_4 = ((unsigned int)(local_24) == (unsigned int)(local_8));
        if (((unsigned int)(local_24) == (unsigned int)(local_8))) {
            local_8 = var13;
        }
        var14 = ((zf_4 == 0) ? t143 : var9);
        local_2c = var14;
        local_24 = var14;
        if (((unsigned long)((unsigned char)((zf_4 & 1))) != 0)) {
            break;
        }
    }
    local_1c = local_2c;
    local_20 = local_8;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(previous)) ^ local_20));
}
atomic_flag_round_trip pass 23 lines
// glaurung: atomic_flag_round_trip @ 0x12a0
__attribute__((no_stack_protector)) int32_t atomic_flag_round_trip(int32_t arg0) {
    int first;
    int second;
    signed char local_16;
    unsigned char local_8[1];
    signed char local_e;
    long t137;
    long t142;
    // x86-64 prologue: save rbp
    *(signed char *)(&local_8[0]) = 0;
    t137 = *(char *)(&local_8[0]);
    *(signed char *)(&local_8[0]) = 1;
    local_e = t137;
    first = ((unsigned int)((unsigned char)(local_e)) & 1);
    t142 = *(char *)(&local_8[0]);
    *(signed char *)(&local_8[0]) = 1;
    local_16 = t142;
    second = ((unsigned int)((unsigned char)(local_16)) & 1);
    *(signed char *)(&local_8[0]) = 0;
    // x86-64 epilogue: restore rbp
    return (unsigned int)((((((unsigned long)((unsigned int)(first)) != 0) ? 1 : 0) * 10) + (((unsigned long)((unsigned int)(second)) != 0) ? 1 : 0)));
}
atomic_increment pass 30 lines
// glaurung: atomic_increment @ 0x1100
int32_t atomic_increment(int32_t arg0, int32_t arg1) {
    int index;
    int local_18;
    int local_1c;
    int local_20;
    int local_4;
    long t138;
    // x86-64 prologue: save rbp
    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 (index = 0; (index < arg1); index++) {
        local_18 = 1;
        t138 = (unsigned long)((unsigned int)(arg0));
        arg0 = (arg0 + (unsigned int)(local_18));
        local_1c = t138;
    }
    local_20 = arg0;
    local_4 = local_20;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}

clang -O2

4/4
atomic_compare_exchange_loop pass 50 lines
// glaurung: atomic_compare_exchange_loop @ 0x1180
int32_t atomic_compare_exchange_loop(int32_t arg0, int32_t arg1) {
    int expected;
    int attempts;
    int local_4;
    long t0;
    long t137;
    long t157;
    long var0;
    long var2;
    long var4;
    long var6;
    long var8;
    long zf_2;
    long zf_5;
    var0 = (unsigned long)((unsigned int)(arg0));
    local_4 = arg0;
    var2 = 0;
    t0 = (unsigned long)((unsigned int)(local_4));
    zf_2 = ((unsigned int)(arg0) == (unsigned int)(local_4));
    if (((unsigned int)(arg0) == (unsigned int)(local_4))) {
        local_4 = arg1;
    }
    expected = ((zf_2 == 0) ? t0 : var0);
    var4 = var2;
    if (zf_2) {
        var6 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_4)) + ((unsigned long)((unsigned int)(local_4)) * 4))));
        return (unsigned int)((var4 + (var6 * 2)));
    }
    var8 = 0;
    while (1) {
        var4 = (unsigned long)((unsigned int)((var8 + 1)));
        t137 = (unsigned long)((unsigned int)(local_4));
        zf_5 = ((unsigned int)(expected) == (unsigned int)(local_4));
        if (((unsigned int)(expected) == (unsigned int)(local_4))) {
            local_4 = arg1;
        }
        expected = ((zf_5 == 0) ? t137 : expected);
        if ((zf_5 != 0)) {
            break;
        }
        t157 = (unsigned long)((unsigned int)(var8));
        var8 = (unsigned long)((unsigned int)(var4));
        if (((unsigned long)(63) <= (unsigned long)(t157))) {
            break;
        }
    }
    var6 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_4)) + ((unsigned long)((unsigned int)(local_4)) * 4))));
    return (unsigned int)((var4 + (var6 * 2)));
}
atomic_exchange_and_or pass 11 lines
// glaurung: atomic_exchange_and_or @ 0x11c0
int32_t atomic_exchange_and_or(int32_t arg0) {
    int previous;
    int local_4;
    long t0;
    local_4 = arg0;
    t0 = (unsigned long)((unsigned int)(local_4));
    local_4 = (arg0 ^ 0x5a5a);
    local_4 = (local_4 | 1);
    return (unsigned int)(((unsigned long)((unsigned int)(local_4)) ^ t0));
}
atomic_flag_round_trip pass 19 lines
// glaurung: atomic_flag_round_trip @ 0x11e0
int32_t atomic_flag_round_trip(int32_t arg0) {
    signed char local_8;
    long ret;
    long t0;
    int var11;
    long var4;
    int var8;
    local_8 = 0;
    ret = ((ret & -256) | 1);
    t0 = (unsigned long)((unsigned char)(local_8));
    local_8 = ret;
    ret = t0;
    local_8 = var4;
    var8 = (unsigned int)((unsigned char)((t0 & 1)));
    local_8 = 0;
    var11 = (var8 + var8);
    return (unsigned int)((unsigned char)(((((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var11)) + ((unsigned long)((unsigned int)(var11)) * 4)))) & 255) | 1) & 255)));
}
atomic_increment pass 39 lines
// glaurung: atomic_increment @ 0x1100
int32_t atomic_increment(int32_t arg0, int32_t arg1) {
    int index;
    long ret;
    long var3;
    long var5;
    int var6;
    int var7;
    ret = 0xffffffff;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return ret;
    }
    if (((unsigned long)((unsigned int)(arg1)) != 0)) {
        var3 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) & 7)));
        if (((unsigned long)(7) <= (unsigned long)((unsigned long)((unsigned int)((arg1 - 1)))))) {
            var5 = (unsigned long)((unsigned int)((arg1 & -8)));
            do {
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                arg0 = (arg0 + 1);
                var6 = (var5 - 8);
                var5 = (unsigned long)((unsigned int)(var6));
            } while (((unsigned long)((unsigned int)(var6)) != 0));
        }
        if (((unsigned long)((unsigned int)(var3)) != 0)) {
            do {
                arg0 = (arg0 + 1);
                var7 = (var3 - 1);
                var3 = (unsigned long)((unsigned int)(var7));
            } while (((unsigned long)((unsigned int)(var7)) != 0));
        }
    }
    return (unsigned int)(arg0);
}

gcc -O0

4/4
atomic_compare_exchange_loop pass 61 lines
// glaurung: atomic_compare_exchange_loop @ 0x11ab
int32_t atomic_compare_exchange_loop(int32_t arg0, int32_t arg1) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int expected;
    int attempts;
    int local_28;
    char * local_10;
    char * local_18;
    char * local_20;
    unsigned char local_30[48];
    long ret;
    long t147;
    long var12;
    char * var13;
    long var15;
    long var17;
    long var19;
    int var29;
    long zf_4;
    *(long *)((&local_30[0] + 40)) = (long)((long)(0x28));
    expected = arg0;
    attempts = 0;
    local_20 = (char *)&local_30[0];
    local_28 = arg0;
    *(int *)((local_20)) = local_28;
    goto L_11f8;
    L_11f4: ;
    attempts = (attempts + 1);
    L_11f8: ;
    local_18 = (char *)&local_30[0];
    local_28 = arg1;
    var12 = (unsigned long)((unsigned int)(local_28));
    var13 = local_18;
    var15 = (unsigned long)((unsigned int)(expected));
    t147 = *(int *)(local_18);
    zf_4 = ((unsigned int)(expected) == (unsigned int)(t147));
    if (((unsigned int)(expected) == (unsigned int)(t147))) {
        *(int *)((var13)) = var12;
    }
    var17 = (unsigned long)((unsigned int)(((zf_4 == 0) ? t147 : var15)));
    var19 = (zf_4 & 255);
    if (((unsigned long)((unsigned char)((var19 & 255))) == 0)) {
        expected = var17;
    }
    if (((unsigned long)((unsigned char)(((unsigned long)((unsigned int)((var19 ^ 1))) & 255))) != 0)) {
        if ((((unsigned long)((unsigned int)(attempts)) == 63) | ((long)(attempts) < 63))) {
            goto L_11f4;
        }
    }
    local_10 = (char *)&local_30[0];
    local_28 = *(int *)(local_10);
    var29 = ((unsigned int)(((unsigned long)((unsigned int)(local_28)) << 2)) + (unsigned int)(local_28));
    ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(attempts)) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var29)) + (unsigned long)((unsigned int)(var29))))))));
    if ((*(long *)((&local_30[0] + 40)) == 0x28)) {
        // x86-64 epilogue: restore rbp
        return ret;
    }
    __stack_chk_fail();
    // x86-64 epilogue: restore rbp
    return ret;
}
atomic_exchange_and_or pass 33 lines
// glaurung: atomic_exchange_and_or @ 0x126b
int32_t atomic_exchange_and_or(int32_t arg0) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int local_28;
    int __atomic_exchange_val;
    int previous;
    char * local_10;
    char * local_18;
    char * local_20;
    unsigned char local_30[48];
    long ret;
    long t138;
    // x86-64 prologue: save rbp, frame 64 bytes
    *(long *)((&local_30[0] + 40)) = (long)((long)(0x28));
    local_20 = (char *)&local_30[0];
    local_28 = arg0;
    *(int *)((local_20)) = local_28;
    local_18 = (char *)&local_30[0];
    __atomic_exchange_val = ((unsigned int)(arg0) ^ 0x5a5a);
    t138 = *(int *)(local_18);
    *(int *)((local_18)) = __atomic_exchange_val;
    local_28 = t138;
    previous = local_28;
    *(int *)(&local_30[0]) = (*(int *)(&local_30[0]) | 1);
    local_10 = (char *)&local_30[0];
    local_28 = *(int *)(local_10);
    ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_28)) ^ previous)));
    if ((*(long *)((&local_30[0] + 40)) != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}
atomic_flag_round_trip pass 31 lines
// glaurung: atomic_flag_round_trip @ 0x12fd
int32_t atomic_flag_round_trip(int32_t arg0) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int first;
    int second;
    unsigned char local_11[1];
    long local_8;
    long ret;
    long t0;
    long t137;
    // x86-64 prologue: save rbp, frame 48 bytes
    local_8 = (long)(0x28);
    *(signed char *)(&local_11[0]) = 0;
    ret = 1;
    t0 = *(char *)(&local_11[0]);
    *(signed char *)(&local_11[0]) = 1;
    ret = t0;
    first = (unsigned char)((t0 & 255));
    ret = 1;
    t137 = *(char *)(&local_11[0]);
    *(signed char *)(&local_11[0]) = 1;
    ret = t137;
    second = (unsigned char)((t137 & 255));
    *(signed char *)(&local_11[0]) = 0;
    ret = (unsigned long)((unsigned int)((((unsigned long)((unsigned int)(second)) != 0) + (((unsigned long)((unsigned int)(first)) == 0) ? 0 : 10))));
    if ((local_8 != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}
atomic_increment pass 30 lines
// glaurung: atomic_increment @ 0x1119
int32_t atomic_increment(int32_t arg0, int32_t arg1) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int local_20;
    int index;
    char * local_10;
    char * local_18;
    unsigned char local_24[36];
    long ret;
    // x86-64 prologue: save rbp, frame 64 bytes
    *(long *)((&local_24[0] + 28)) = (long)((long)(0x28));
    if ((((long)(arg1) < 0) || (((unsigned long)((unsigned int)(arg1)) != 16) && (16 <= (long)(arg1))))) {
        ret = 0xffffffff;
    } else {
        local_18 = (char *)&local_24[0];
        local_20 = arg0;
        *(int *)((local_18)) = local_20;
        for (index = 0; (index < arg1); index++) {
            *(int *)(&local_24[0]) = (*(int *)(&local_24[0]) + 1);
        }
        local_10 = (char *)&local_24[0];
        local_20 = *(int *)(local_10);
        ret = (unsigned long)((unsigned int)(local_20));
    }
    if ((*(long *)((&local_24[0] + 28)) != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}

← 213 fixtures