Fixture 71
compound interest
C · 2 functions · 4 lanes · 8 of 8 function-lanes behave identically
All 4 lanes recompile and return the same results as the original.
Compound growth in Q16.16 with per-period compounding, plus a future value for a regular contribution stream. The repeated multiply-accumulate is the finance analogue of a stencil: small body, exact carry requirements.
#include <stdint.h>
/* Compound growth in Q16.16 with per-period compounding, plus a future value
* for a regular contribution stream. The repeated multiply-accumulate is the
* finance analogue of a stencil: small body, exact carry requirements. */
#define FIN_PERIODS_MAX 32
static int32_t fin_mul_q16(int32_t left, int32_t right) {
return (int32_t)(((int64_t)left * (int64_t)right) >> 16);
}
__attribute__((noinline)) int32_t
compound_balance(int32_t principal, int32_t rate_per_period, int32_t periods) {
int32_t balance = principal;
int32_t period;
if (principal < 0 || rate_per_period < 0 || rate_per_period > 65536 ||
periods < 0 || periods > FIN_PERIODS_MAX) {
return -1;
}
for (period = 0; period < periods; ++period) {
balance += fin_mul_q16(balance, rate_per_period);
}
return balance;
}
__attribute__((noinline)) int32_t
annuity_future_value(int32_t contribution, int32_t rate_per_period,
int32_t periods) {
int32_t balance = 0;
int32_t period;
if (contribution < 0 || rate_per_period < 0 || rate_per_period > 65536 ||
periods < 0 || periods > FIN_PERIODS_MAX) {
return -1;
}
for (period = 0; period < periods; ++period) {
balance += fin_mul_q16(balance, rate_per_period);
balance += contribution;
}
return balance;
} 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
2/2annuity_future_value pass 43 lines
// glaurung: annuity_future_value @ 0x11c0
int32_t annuity_future_value(int32_t arg0, int32_t arg1, int32_t arg2) {
extern int fin_mul_q16(int, int);
int balance;
int period;
int local_4;
int var1;
// x86-64 prologue: save rbp, frame 32 bytes
balance = 0;
if (((long)(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 (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((long)(arg2) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg2)) == 32) | ((long)(arg2) < 32)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
for (period = 0; (period < arg2); period++) {
var1 = fin_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
balance = (var1 + balance);
balance = ((unsigned int)(arg0) + balance);
}
local_4 = balance;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} compound_balance pass 42 lines
// glaurung: compound_balance @ 0x1100
int32_t compound_balance(int32_t arg0, int32_t arg1, int32_t arg2) {
extern int fin_mul_q16(int, int);
int balance;
int period;
int local_4;
int var2;
// x86-64 prologue: save rbp, frame 32 bytes
balance = arg0;
if (((long)(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 (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((long)(arg2) < 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned long)((unsigned int)(arg2)) == 32) | ((long)(arg2) < 32)) == 0)) {
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
for (period = 0; (period < arg2); period++) {
var2 = fin_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
balance = (var2 + balance);
}
local_4 = balance;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} clang -O2
2/2annuity_future_value pass 61 lines
// glaurung: annuity_future_value @ 0x11c0
int32_t annuity_future_value(int32_t arg0, int32_t arg1, int32_t arg2) {
int balance;
int period;
long var11;
long var13;
long var16;
int var23;
int var31;
int var39;
int var47;
int var48;
long var51;
int var58;
int var59;
long var7;
balance = 0xffffffff;
if (((((unsigned long)((unsigned int)(arg2)) == 32) | ((long)(arg2) < 32)) != 0)) {
balance = 0xffffffff;
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
return balance;
}
balance = 0xffffffff;
if (((long)((int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) | arg0))) | arg2))) < 0)) {
return balance;
}
if ((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0))) {
return 0;
}
var7 = (long)(arg1);
var11 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg2)) & 3)));
if (((unsigned long)(3) <= (unsigned long)((unsigned long)((unsigned int)((arg2 - 1)))))) {
var13 = (unsigned long)((unsigned int)((arg2 & -4)));
var16 = 0;
do {
var23 = ((unsigned int)(((long)((int)(var16)) + arg0)) + ((unsigned long)(((long)((int)(var16)) * var7)) >> 16));
var31 = ((unsigned int)(((long)((int)(var23)) + arg0)) + ((unsigned long)(((long)((int)(var23)) * var7)) >> 16));
var39 = ((unsigned int)(((long)((int)(var31)) + arg0)) + ((unsigned long)(((long)((int)(var31)) * var7)) >> 16));
var47 = ((unsigned int)(((long)((int)(var39)) + arg0)) + ((unsigned long)(((long)((int)(var39)) * var7)) >> 16));
balance = (unsigned long)((unsigned int)(var47));
var48 = (var13 - 4);
var13 = (unsigned long)((unsigned int)(var48));
var16 = (unsigned long)((unsigned int)(var47));
} while (((unsigned long)((unsigned int)(var48)) != 0));
} else {
balance = 0;
}
if (((unsigned long)((unsigned int)(var11)) == 0)) {
return balance;
}
var51 = balance;
do {
var58 = ((unsigned int)(((long)((int)(var51)) + arg0)) + ((unsigned long)(((long)((int)(var51)) * var7)) >> 16));
balance = (unsigned long)((unsigned int)(var58));
var59 = (var11 - 1);
var11 = (unsigned long)((unsigned int)(var59));
var51 = (unsigned long)((unsigned int)(var58));
} while (((unsigned long)((unsigned int)(var59)) != 0));
}
return balance;
} compound_balance pass 62 lines
// glaurung: compound_balance @ 0x1100
int32_t compound_balance(int32_t arg0, int32_t arg1, int32_t arg2) {
int period;
long ret;
long var10;
long var11;
long var13;
long var14;
int var19;
int var25;
int var31;
int var37;
int var39;
long var42;
int var47;
int var48;
long var6;
ret = 0xffffffff;
if (((((unsigned long)((unsigned int)(arg2)) == 32) | ((long)(arg2) < 32)) != 0)) {
ret = 0xffffffff;
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
return ret;
}
ret = 0xffffffff;
if (((long)((int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) | arg0))) | arg2))) < 0)) {
return ret;
}
if ((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0))) {
return (unsigned int)(arg0);
}
var6 = (long)(arg1);
var10 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg2)) & 3)));
var11 = (unsigned long)((unsigned int)(arg0));
if (((unsigned long)(3) <= (unsigned long)((unsigned long)((unsigned int)((arg2 - 1)))))) {
var13 = (unsigned long)((unsigned int)((arg2 & -4)));
var14 = (unsigned long)((unsigned int)(arg0));
do {
var19 = ((long)((int)(var14)) + ((unsigned long)(((long)((int)(var14)) * var6)) >> 16));
var25 = ((long)((int)(var19)) + ((unsigned long)(((long)((int)(var19)) * var6)) >> 16));
var31 = ((long)((int)(var25)) + ((unsigned long)(((long)((int)(var25)) * var6)) >> 16));
var37 = ((long)((int)(var31)) + ((unsigned long)(((long)((int)(var31)) * var6)) >> 16));
var39 = (var13 - 4);
var13 = (unsigned long)((unsigned int)(var39));
var14 = (unsigned long)((unsigned int)(var37));
var11 = (unsigned long)((unsigned int)(var37));
} while (((unsigned long)((unsigned int)(var39)) != 0));
}
ret = (unsigned long)((unsigned int)(var11));
if (((unsigned long)((unsigned int)(var10)) == 0)) {
return ret;
}
var42 = (unsigned long)((unsigned int)(var11));
do {
var47 = ((long)((int)(var42)) + ((unsigned long)(((long)((int)(var42)) * var6)) >> 16));
ret = (unsigned long)((unsigned int)(var47));
var48 = (var10 - 1);
var10 = (unsigned long)((unsigned int)(var48));
var42 = (unsigned long)((unsigned int)(var47));
} while (((unsigned long)((unsigned int)(var48)) != 0));
}
return ret;
} gcc -O0
2/2annuity_future_value pass 36 lines
// glaurung: annuity_future_value @ 0x118b
int32_t annuity_future_value(int32_t arg0, int32_t arg1, int32_t arg2) {
extern int fin_mul_q16(int, int);
int balance;
int period;
int var3;
// x86-64 prologue: save rbp, frame 32 bytes
balance = 0;
if (((long)(arg0) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((long)(arg1) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((long)(arg2) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg2)) == 32) | ((long)(arg2) < 32)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
for (period = 0; (period < arg2); period++) {
var3 = fin_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
balance = (balance + var3);
balance = (balance + (unsigned int)(arg0));
}
// x86-64 epilogue: restore rbp
return (unsigned int)(balance);
} compound_balance pass 35 lines
// glaurung: compound_balance @ 0x111c
int32_t compound_balance(int32_t arg0, int32_t arg1, int32_t arg2) {
extern int fin_mul_q16(int, int);
int balance;
int period;
int var4;
// x86-64 prologue: save rbp, frame 32 bytes
balance = arg0;
if (((long)(arg0) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((long)(arg1) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((long)(arg2) < 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
if (((((unsigned long)((unsigned int)(arg2)) == 32) | ((long)(arg2) < 32)) == 0)) {
// x86-64 epilogue: restore rbp
return 0xffffffff;
}
for (period = 0; (period < arg2); period++) {
var4 = fin_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
balance = (balance + var4);
}
// x86-64 epilogue: restore rbp
return (unsigned int)(balance);
} gcc -O2
2/2annuity_future_value pass 29 lines
// glaurung: annuity_future_value @ 0x1140
int32_t annuity_future_value(int32_t arg0, int32_t arg1, int32_t arg2) {
int period;
int balance;
long ret;
long var1;
int var8;
if (((long)(arg0) < 0)) {
return 0xffffffff;
}
if (((unsigned long)(32) < (unsigned long)((unsigned long)((unsigned int)(arg2))))) {
return 0xffffffff;
}
if (((unsigned long)(0x10000) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return 0xffffffff;
}
if (((unsigned long)((unsigned int)(arg2)) == 0)) {
return 0;
}
var1 = (long)(arg1);
period = 0;
ret = 0;
do {
var8 = (period + 1);
period = (unsigned long)((unsigned int)(var8));
ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)((ret + ((long)(((long)((int)(ret)) * var1)) >> 16)))) + arg0)));
} while (((unsigned int)(arg2) != (unsigned int)(var8)));
return ret;
} compound_balance pass 35 lines
// glaurung: compound_balance @ 0x1100
int32_t compound_balance(int32_t arg0, int32_t arg1, int32_t arg2) {
int balance;
int period;
long ret;
long var0;
long var1;
long var4;
long var5;
int var7;
var0 = (unsigned long)((unsigned int)(arg0));
if (((long)(arg0) < 0)) {
return 0xffffffff;
}
if (((unsigned long)(32) < (unsigned long)((unsigned long)((unsigned int)(arg2))))) {
return 0xffffffff;
}
if (((unsigned long)(0x10000) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return 0xffffffff;
}
ret = (unsigned long)((unsigned int)(arg0));
if (((unsigned long)((unsigned int)(arg2)) != 0)) {
var1 = (long)(arg1);
var4 = var0;
var5 = 0;
do {
var7 = (var5 + 1);
balance = (var4 + ((long)(((long)((int)(var4)) * var1)) >> 16));
ret = (unsigned long)((unsigned int)(balance));
var4 = (unsigned long)((unsigned int)(balance));
var5 = (unsigned long)((unsigned int)(var7));
} while (((unsigned int)(arg2) != (unsigned int)(var7)));
}
return ret;
}