Fixture 112
recursion shapes
C · 4 functions · 4 lanes · 13 of 16 function-lanes behave identically
3 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (3/4), gcc-O0 (3/4), gcc-O2 (3/4).
Four recursion shapes that lower differently: mutual recursion (two frames that cannot be inlined into each other), self tail recursion (usually turned into a loop at -O2), non-tail recursion (a real frame), and an accumulator form that is tail-recursive by construction.
#include <stdint.h>
/* Four recursion shapes that lower differently: mutual recursion (two frames
* that cannot be inlined into each other), self tail recursion (usually turned
* into a loop at -O2), non-tail recursion (a real frame), and an accumulator
* form that is tail-recursive by construction. */
static int32_t is_odd_helper(int32_t value, int32_t fuel);
static int32_t is_even_helper(int32_t value, int32_t fuel) {
if (fuel <= 0) {
return -1;
}
if (value == 0) {
return 1;
}
return is_odd_helper(value - 1, fuel - 1);
}
static int32_t is_odd_helper(int32_t value, int32_t fuel) {
if (fuel <= 0) {
return -1;
}
if (value == 0) {
return 0;
}
return is_even_helper(value - 1, fuel - 1);
}
__attribute__((noinline)) int32_t mutual_parity(int32_t value) {
if (value < 0 || value > 64) {
return -1;
}
return is_even_helper(value, 128);
}
__attribute__((noinline)) int32_t tail_countdown(int32_t value, int32_t total) {
if (value <= 0) {
return total;
}
return tail_countdown(value - 1, total + value); /* self tail call */
}
__attribute__((noinline)) int32_t nontail_depth(int32_t value) {
if (value <= 0) {
return 0;
}
if (value > 32) {
return -1;
}
/* The addition happens after the call returns: a real frame. */
return 1 + nontail_depth(value - 1);
}
__attribute__((noinline)) int32_t
recursion_entry(int32_t selector, int32_t value) {
if (value < 0 || value > 32) {
return -1;
}
switch (selector & 3) {
case 0:
return mutual_parity(value);
case 1:
return tail_countdown(value, 0);
default:
return nontail_depth(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 -O0
3/4mutual_parity pass 21 lines
// glaurung: mutual_parity @ 0x1130
int32_t mutual_parity(int32_t arg0) {
extern int is_even_helper(int, int);
int local_4;
int var0;
// x86-64 prologue: save rbp, frame 16 bytes
if (((long)(arg0) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg0)) == 64) | ((long)(arg0) < 64)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
var0 = is_even_helper((unsigned long)((unsigned int)(arg0)), 128);
local_4 = var0;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} nontail_depth pass 15 lines
// glaurung: nontail_depth @ 0x1220
int32_t nontail_depth(int32_t arg0) {
int var2;
// x86-64 prologue: save rbp, frame 16 bytes
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) == 0)) {
if ((((unsigned long)((unsigned int)(arg0)) == 32) | ((long)(arg0) < 32))) {
var2 = ((int (*)(int))nontail_depth)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - 1))));
return (unsigned int)((var2 + 1));
} else {
return (unsigned int)(-1);
}
} else {
return 0;
}
} recursion_entry pass 37 lines
// glaurung: recursion_entry @ 0x1280
int32_t recursion_entry(int32_t arg0, int32_t arg1) {
extern int mutual_parity(int);
extern int nontail_depth(int);
extern int tail_countdown(int, int);
int local_10;
int local_4;
int var1;
int var11;
int var3;
int var9;
// x86-64 prologue: save rbp, frame 16 bytes
if (((long)(arg1) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg1)) == 32) | ((long)(arg1) < 32)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
var1 = ((unsigned int)(arg0) & 3);
local_10 = var1;
if (((unsigned long)((unsigned int)(var1)) == 0)) {
var3 = mutual_parity((unsigned long)((unsigned int)(arg1)));
return (unsigned int)(var3);
} else {
if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_10)) - 1))) == 0)) {
var9 = tail_countdown((unsigned long)((unsigned int)(arg1)), 0);
return (unsigned int)(var9);
} else {
var11 = nontail_depth((unsigned long)((unsigned int)(arg1)));
return (unsigned int)(var11);
}
}
} tail_countdown fail 11 lines
// glaurung: tail_countdown @ 0x11e0
int32_t tail_countdown(int32_t arg0, int32_t arg1) {
int var4;
// x86-64 prologue: save rbp, frame 16 bytes
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) == 0)) {
var4 = ((int (*)(int, int))tail_countdown)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - 1))), (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) + arg0))));
return (unsigned int)(var4);
} else {
return (unsigned int)(arg1);
}
} gcc -O0
3/4mutual_parity pass 17 lines
// glaurung: mutual_parity @ 0x11df
int32_t mutual_parity(int32_t arg0) {
extern int is_even_helper(int, int);
int ret;
// x86-64 prologue: save rbp, frame 16 bytes
if (((long)(arg0) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg0)) == 64) | ((long)(arg0) < 64)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
ret = is_even_helper((unsigned long)((unsigned int)(arg0)), 128);
// x86-64 epilogue: restore rbp
return ret;
} nontail_depth pass 15 lines
// glaurung: nontail_depth @ 0x1248
int32_t nontail_depth(int32_t arg0) {
int var3;
// x86-64 prologue: save rbp, frame 16 bytes
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) == 0)) {
if ((((unsigned long)((unsigned int)(arg0)) == 32) | ((long)(arg0) < 32))) {
var3 = ((int (*)(int))nontail_depth)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - 1))));
return (unsigned int)((var3 + 1));
} else {
return 0xffffffff;
}
} else {
return 0;
}
} recursion_entry pass 32 lines
// glaurung: recursion_entry @ 0x1283
int32_t recursion_entry(int32_t arg0, int32_t arg1) {
extern int mutual_parity(int);
extern int nontail_depth(int);
extern int tail_countdown(int, int);
long var2;
int var4;
int var6;
int var8;
// x86-64 prologue: save rbp, frame 16 bytes
if (((long)(arg1) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg1)) == 32) | ((long)(arg1) < 32)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
var2 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3)));
if (((unsigned long)((unsigned int)(var2)) == 0)) {
var4 = mutual_parity((unsigned long)((unsigned int)(arg1)));
return var4;
} else {
if (((unsigned long)((unsigned int)(var2)) == 1)) {
var6 = tail_countdown((unsigned long)((unsigned int)(arg1)), 0);
return var6;
} else {
var8 = nontail_depth((unsigned long)((unsigned int)(arg1)));
return var8;
}
}
} tail_countdown fail 11 lines
// glaurung: tail_countdown @ 0x1212
int32_t tail_countdown(int32_t arg0, int32_t arg1) {
int var7;
// x86-64 prologue: save rbp, frame 16 bytes
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) == 0)) {
var7 = ((int (*)(int, int))tail_countdown)((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - 1))), (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) + (unsigned long)((unsigned int)(arg0))))));
return var7;
} else {
return (unsigned int)(arg1);
}
} gcc -O2
3/4mutual_parity pass 33 lines
// glaurung: mutual_parity @ 0x11c0
int32_t mutual_parity(int32_t arg0) {
extern int is_even_helper(long, int);
int ret;
if (((unsigned long)(64) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
return 0xffffffff;
}
ret = 1;
if (((unsigned long)((unsigned int)(arg0)) != 0)) {
ret = 0;
if (((unsigned long)((unsigned int)(arg0)) == 1)) {
return ret;
}
ret = 1;
if (((unsigned long)((unsigned int)(arg0)) == 2)) {
return ret;
}
ret = 0;
if (((unsigned long)((unsigned int)(arg0)) == 3)) {
return ret;
}
ret = 1;
if (((unsigned long)((unsigned int)(arg0)) == 4)) {
return ret;
}
ret = 0;
if (((unsigned long)((unsigned int)(arg0)) != 5)) {
ret = is_even_helper((unsigned long)((unsigned int)((arg0 - 6))), 122);
return ret;
}
}
return ret;
} nontail_depth pass 7 lines
// glaurung: nontail_depth @ 0x1240
int32_t nontail_depth(int32_t arg0) {
if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
return 0;
}
return ((((unsigned long)((unsigned int)(arg0)) == 32) | ((long)(arg0) < 32)) ? arg0 : 0xffffffff);
} recursion_entry fail 24 lines
// glaurung: recursion_entry @ 0x1260
int32_t recursion_entry(int32_t arg0, int32_t arg1) {
extern int mutual_parity(int);
extern int nontail_depth(int);
extern int tail_countdown(int, int);
int ret;
int var1;
long var2;
if (((unsigned long)(32) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return 0xffffffff;
}
var1 = ((unsigned int)(arg0) & 3);
var2 = (unsigned long)((unsigned int)(var1));
if (((unsigned long)((unsigned int)(var1)) == 0)) {
ret = ((int (*)(void))mutual_parity)();
return ret;
}
if (((unsigned long)((unsigned int)(var2)) == 1)) {
ret = ((int (*)(void))tail_countdown)();
return ret;
}
ret = nontail_depth((unsigned long)((unsigned int)(arg1)));
return ret;
} tail_countdown pass 21 lines
// glaurung: tail_countdown @ 0x1220
int32_t tail_countdown(int32_t arg0, int32_t arg1) {
long ret;
long var0;
long var1;
int var2;
int var3;
var0 = (unsigned long)((unsigned int)(arg1));
ret = (unsigned long)((unsigned int)(arg1));
if ((((unsigned long)((unsigned int)(arg0)) != 0) && (0 <= (long)(arg0)))) {
var1 = (unsigned long)((unsigned int)(arg0));
do {
var2 = (var0 + var1);
var0 = (unsigned long)((unsigned int)(var2));
var3 = (var1 - 1);
var1 = (unsigned long)((unsigned int)(var3));
ret = (unsigned long)((unsigned int)(var2));
} while (((unsigned long)((unsigned int)(var3)) != 0));
}
return ret;
} clang -O2
4/4mutual_parity pass 33 lines
// glaurung: mutual_parity @ 0x1130
int32_t mutual_parity(int32_t arg0) {
long ret;
long var1;
long var2;
int var3;
ret = 0xffffffff;
if (((unsigned long)(64) < (unsigned long)((unsigned long)((unsigned int)(arg0))))) {
return ret;
}
var1 = 0;
L_113c: ;
while (1) {
var2 = (unsigned long)((unsigned int)((arg0 + var1)));
if (((unsigned long)((unsigned long)((unsigned int)(var2))) <= (unsigned long)(15))) {
break;
}
var3 = (var1 - 16);
var1 = (unsigned long)((unsigned int)(var3));
if (((unsigned long)((unsigned int)(var3)) != 0xffffff80)) {
goto L_113c;
}
goto L_114c;
}
ret = 1;
if (((((unsigned long)(0x5555) >> (var2 & 31)) & 1) == 0)) {
return 0;
}
L_115d: ;
return ret;
L_114c: ;
goto L_115d;
} nontail_depth pass 24 lines
// glaurung: nontail_depth @ 0x1190
int32_t nontail_depth(int32_t arg0) {
long ret;
int var1;
long var3;
ret = 0;
if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
return ret;
}
L_11a0: ;
while (((unsigned long)((unsigned long)((unsigned int)(arg0))) <= (unsigned long)(32))) {
var1 = (ret + 1);
ret = (unsigned long)((unsigned int)(var1));
if (((unsigned int)(arg0) != (unsigned int)(var1))) {
goto L_11a0;
} else {
var3 = 0;
ret = (unsigned long)((unsigned int)(arg0));
}
return (unsigned int)((ret + var3));
}
var3 = 0xffffffff;
return (unsigned int)((ret + 0xffffffff));
} recursion_entry pass 22 lines
// glaurung: recursion_entry @ 0x11c0
int32_t recursion_entry(int32_t arg0, int32_t arg1) {
extern int mutual_parity(int);
extern int nontail_depth(int);
extern int tail_countdown(int, int);
int ret;
long var1;
if (((unsigned long)(32) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return 0xffffffff;
}
var1 = (unsigned long)((unsigned int)((arg0 & 3)));
if (((unsigned long)((unsigned int)(var1)) == 1)) {
ret = tail_countdown((unsigned long)((unsigned int)(arg1)), 0);
return ret;
}
if (((unsigned long)((unsigned int)(var1)) != 0)) {
ret = nontail_depth((unsigned long)((unsigned int)(arg1)));
return ret;
}
ret = mutual_parity((unsigned long)((unsigned int)(arg1)));
return ret;
} tail_countdown pass 11 lines
// glaurung: tail_countdown @ 0x1170
int32_t tail_countdown(int32_t arg0, int32_t arg1) {
long ret;
long var0;
ret = (unsigned long)((unsigned int)(arg1));
if (((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0)) == 0)) {
var0 = (unsigned long)((unsigned int)((arg0 - 1)));
ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)((ret + arg0))) + (unsigned long)((unsigned int)((var0 * var0)))))) - ((unsigned long)(((unsigned long)((unsigned int)((arg0 - 2))) * var0)) >> 1))));
}
return ret;
}