Fixture 107
short circuit
C · 3 functions · 4 lanes · 12 of 12 function-lanes behave identically
All 4 lanes recompile and return the same results as the original.
&& and || impose a sequence point and evaluate the right operand only when needed. Counting the evaluations proves the branch was preserved rather than flattened into an arithmetic combination.
#include <stdint.h>
/* && and || impose a sequence point and evaluate the right operand only when
* needed. Counting the evaluations proves the branch was preserved rather than
* flattened into an arithmetic combination. */
static int32_t observe(int32_t value, int32_t *counter) {
*counter += 1;
return value;
}
__attribute__((noinline)) int32_t
short_circuit_and(int32_t a, int32_t b, int32_t *evaluations) {
if (evaluations == 0) {
return -1;
}
*evaluations = 0;
if (observe(a, evaluations) && observe(b, evaluations)) {
return 1;
}
return 0;
}
__attribute__((noinline)) int32_t
short_circuit_or(int32_t a, int32_t b, int32_t *evaluations) {
if (evaluations == 0) {
return -1;
}
*evaluations = 0;
if (observe(a, evaluations) || observe(b, evaluations)) {
return 1;
}
return 0;
}
__attribute__((noinline)) int32_t
guarded_dereference(const int32_t *pointer, int32_t index, int32_t bound) {
/* The classic guard: the right operand must not run when the left fails. */
if (pointer != 0 && index >= 0 && index < bound && bound <= 16) {
return pointer[index];
}
return -1;
} 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/3guarded_dereference pass 28 lines
// glaurung: guarded_dereference @ 0x1220
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
int local_4;
// x86-64 prologue: save rbp
if ((arg0 == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((long)(arg1) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if ((arg2 <= arg1)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
local_4 = arg0[(long)(arg1)];
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} short_circuit_and pass 23 lines
// glaurung: short_circuit_and @ 0x1100
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
extern int observe(int, int *);
int var1;
int var3;
// x86-64 prologue: save rbp, frame 32 bytes
if ((arg2 != 0)) {
*(int *)((long)arg2) = 0;
var1 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var1)) == 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
var3 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var3)) == 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
return 1;
} else {
return (unsigned int)(-1);
}
} short_circuit_or pass 25 lines
// glaurung: short_circuit_or @ 0x11a0
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
extern int observe(int, int *);
int local_4;
int var1;
int var3;
if ((arg2 == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
*(int *)((long)arg2) = 0;
var1 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var1)) == 0)) {
var3 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var3)) == 0)) {
goto L_1209;
}
}
// x86-64 epilogue: restore rbp
return 1;
L_1209: ;
// x86-64 epilogue: restore rbp
return 0;
} clang -O2
3/3guarded_dereference pass 17 lines
// glaurung: guarded_dereference @ 0x1160
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
long ret;
ret = 0xffffffff;
if ((arg0 != 0)) {
if (((long)(arg1) < 0)) {
return ret;
}
if ((arg2 <= arg1)) {
return ret;
}
if (((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) != 0)) {
ret = (unsigned long)((unsigned int)(arg0[(unsigned long)((unsigned int)(arg1))]));
}
}
return ret;
} short_circuit_and pass 16 lines
// glaurung: short_circuit_and @ 0x1100
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
long ret;
if ((arg2 == 0)) {
return 0xffffffff;
}
*(int *)(((long)arg2)) = 1;
if (((unsigned long)((unsigned int)(arg0)) != 0)) {
*(int *)(((long)arg2)) = 2;
ret = 1;
if (((unsigned long)((unsigned int)(arg1)) != 0)) {
return ret;
}
}
return 0;
} short_circuit_or pass 14 lines
// glaurung: short_circuit_or @ 0x1130
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
long ret;
if ((arg2 == 0)) {
return 0xffffffff;
}
*(int *)(((long)arg2)) = 1;
ret = 1;
if (((unsigned long)((unsigned int)(arg0)) == 0)) {
*(int *)(((long)arg2)) = 2;
return ((unsigned long)((unsigned int)(arg1)) != 0);
}
return ret;
} gcc -O0
3/3guarded_dereference pass 22 lines
// glaurung: guarded_dereference @ 0x11e8
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
// x86-64 prologue: save rbp
if ((arg0 == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((long)(arg1) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if ((arg2 <= arg1)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
// x86-64 epilogue: restore rbp
return (unsigned int)(arg0[(long)(arg1)]);
} short_circuit_and pass 23 lines
// glaurung: short_circuit_and @ 0x111c
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
extern int observe(int, int *);
int var3;
int var7;
// x86-64 prologue: save rbp, frame 16 bytes
if ((arg2 != 0)) {
*(int *)((long)arg2) = 0;
var3 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var3)) == 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
var7 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var7)) == 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
return 1;
} else {
return 0xffffffff;
}
} short_circuit_or pass 23 lines
// glaurung: short_circuit_or @ 0x1182
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
extern int observe(int, int *);
int var3;
int var7;
if ((arg2 == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
*(int *)((long)arg2) = 0;
var3 = observe((unsigned long)((unsigned int)(arg0)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var3)) != 0)) {
// x86-64 epilogue: restore rbp
return 1;
}
var7 = observe((unsigned long)((unsigned int)(arg1)), (int *)(arg2));
if (((unsigned long)((unsigned int)(var7)) == 0)) {
// x86-64 epilogue: restore rbp
return 0;
}
// x86-64 epilogue: restore rbp
return 1;
} gcc -O2
3/3guarded_dereference pass 13 lines
// glaurung: guarded_dereference @ 0x1180
int32_t guarded_dereference(const int32_t * arg0, int32_t arg1, int32_t arg2) {
if ((arg0 == 0)) {
return 0xffffffff;
}
if (((long)(arg1) < 0)) {
return 0xffffffff;
}
if (((arg2 <= arg1) || ((((unsigned long)((unsigned int)(arg2)) == 16) | ((long)(arg2) < 16)) == 0))) {
return 0xffffffff;
}
return (unsigned int)(arg0[(long)(arg1)]);
} short_circuit_and pass 12 lines
// glaurung: short_circuit_and @ 0x1100
int32_t short_circuit_and(int32_t arg0, int32_t arg1, int32_t * arg2) {
if ((arg2 == 0)) {
return 0xffffffff;
}
if (((unsigned long)((unsigned int)(arg0)) == 0)) {
*(int *)(((long)arg2)) = 1;
return 0;
}
*(int *)(((long)arg2)) = 2;
return ((unsigned long)((unsigned int)(arg1)) != 0);
} short_circuit_or pass 12 lines
// glaurung: short_circuit_or @ 0x1140
int32_t short_circuit_or(int32_t arg0, int32_t arg1, int32_t * arg2) {
if ((arg2 == 0)) {
return 0xffffffff;
}
if (((unsigned long)((unsigned int)(arg0)) != 0)) {
*(int *)(((long)arg2)) = 1;
return 1;
}
*(int *)(((long)arg2)) = 2;
return ((unsigned long)((unsigned int)(arg1)) != 0);
}