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.
#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/4atomic_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/4atomic_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/4atomic_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/4atomic_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;
}