Fixture 72
loan amortization
C · 2 functions · 4 lanes · 8 of 8 function-lanes behave identically
All 4 lanes recompile and return the same results as the original.
A loan amortisation schedule written into caller-owned buffers: each period splits a fixed payment into interest and principal, and the final period absorbs the rounding remainder. Two parallel output buffers advance with one induction variable.
#include <stdint.h>
/* A loan amortisation schedule written into caller-owned buffers: each period
* splits a fixed payment into interest and principal, and the final period
* absorbs the rounding remainder. Two parallel output buffers advance with
* one induction variable. */
#define LOAN_PERIODS_MAX 12
static int32_t loan_mul_q16(int32_t left, int32_t right) {
return (int32_t)(((int64_t)left * (int64_t)right) >> 16);
}
__attribute__((noinline)) int32_t
amortization_schedule(int32_t principal, int32_t rate_per_period,
int32_t payment, int32_t periods, int32_t *interest_out,
int32_t *principal_out) {
int32_t balance = principal;
int32_t period;
if (interest_out == 0 || principal_out == 0 || principal < 0 ||
rate_per_period < 0 || rate_per_period > 65536 || payment <= 0 ||
periods < 0 || periods > LOAN_PERIODS_MAX) {
return -1;
}
for (period = 0; period < periods; ++period) {
int32_t interest = loan_mul_q16(balance, rate_per_period);
int32_t reduction = payment - interest;
if (reduction < 0) {
return -2;
}
if (reduction > balance) {
reduction = balance;
}
interest_out[period] = interest;
principal_out[period] = reduction;
balance -= reduction;
}
return balance;
}
__attribute__((noinline)) int32_t
remaining_balance(int32_t principal, int32_t rate_per_period, int32_t payment,
int32_t periods) {
int32_t balance = principal;
int32_t period;
if (principal < 0 || rate_per_period < 0 || rate_per_period > 65536 ||
payment <= 0 || periods < 0 || periods > LOAN_PERIODS_MAX) {
return -1;
}
for (period = 0; period < periods && balance > 0; ++period) {
int32_t interest = loan_mul_q16(balance, rate_per_period);
int32_t reduction = payment - interest;
if (reduction <= 0) {
return balance;
}
balance -= (reduction > balance) ? balance : reduction;
}
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/2amortization_schedule pass 55 lines
// glaurung: amortization_schedule @ 0x1100
int32_t amortization_schedule(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t * arg4, int32_t * arg5) {
extern int loan_mul_q16(int, int);
int balance;
int period;
int interest;
int reduction;
int local_4;
int var4;
balance = arg0;
if ((arg4 != 0)) {
if ((arg5 != 0)) {
if ((0 <= (long)(arg0))) {
if ((0 <= (long)(arg1))) {
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) != 0)) {
if (((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0)) == 0)) {
if ((0 <= (long)(arg3))) {
if ((((unsigned long)((unsigned int)(arg3)) == 12) | ((long)(arg3) < 12))) {
goto L_1183;
}
}
}
}
}
}
}
}
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
L_1183: ;
period = 0;
L_118a: ;
if ((period < arg3)) {
var4 = loan_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
interest = var4;
reduction = ((unsigned int)(arg2) - interest);
if (((long)(reduction) < 0)) {
local_4 = -2;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned int)(reduction) == (unsigned int)(balance)) | (reduction < balance)) == 0)) {
reduction = balance;
}
arg4[(long)(period)] = interest;
arg5[(long)(period)] = reduction;
balance = ((unsigned int)(balance) - (unsigned int)(reduction));
period = ((unsigned int)(period) + 1);
goto L_118a;
}
local_4 = balance;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} remaining_balance pass 60 lines
// glaurung: remaining_balance @ 0x1240
int32_t remaining_balance(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
extern int loan_mul_q16(int, int);
int balance;
int period;
int interest;
int reduction;
signed char local_25;
int local_2c;
int local_4;
int var8;
balance = arg0;
if ((0 <= (long)(arg0))) {
if ((0 <= (long)(arg1))) {
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) != 0)) {
if (((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0)) == 0)) {
if ((0 <= (long)(arg3))) {
if ((((unsigned long)((unsigned int)(arg3)) == 12) | ((long)(arg3) < 12))) {
goto L_12a5;
}
}
}
}
}
}
local_4 = -1;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
L_12a5: ;
period = 0;
L_12ac: ;
local_25 = 0;
if ((period < arg3)) {
local_25 = ((((unsigned long)((unsigned int)(balance)) == 0) | ((long)(balance) < 0)) == 0);
}
if (((unsigned long)((unsigned char)((local_25 & 1))) == 0)) {
goto L_1339;
}
var8 = loan_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
interest = var8;
reduction = ((unsigned int)(arg2) - interest);
if (((((unsigned long)((unsigned int)(reduction)) == 0) | ((long)(reduction) < 0)) != 0)) {
local_4 = balance;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
}
if (((((unsigned int)(reduction) == (unsigned int)(balance)) | (reduction < balance)) == 0)) {
local_2c = balance;
goto L_1320;
}
local_2c = reduction;
L_1320: ;
balance = ((unsigned int)(balance) - (unsigned int)(local_2c));
period = ((unsigned int)(period) + 1);
goto L_12ac;
L_1339: ;
local_4 = balance;
// x86-64 epilogue: restore rbp
return (unsigned int)(local_4);
} clang -O2
2/2amortization_schedule pass 62 lines
// glaurung: amortization_schedule @ 0x1100
int32_t amortization_schedule(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t * arg4, int32_t * arg5) {
int balance;
int period;
int interest;
int reduction;
long ret;
int var15;
long var16;
int var17;
long var4;
long var5;
ret = 0xffffffff;
if (((unsigned long)(12) < (unsigned long)((unsigned long)((unsigned int)(arg3))))) {
return ret;
}
ret = 0xffffffff;
if ((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0))) {
return ret;
}
ret = 0xffffffff;
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
return ret;
}
ret = 0xffffffff;
if (((long)((int)(((unsigned long)((unsigned int)(arg1)) | arg0))) < 0)) {
return ret;
}
ret = 0xffffffff;
if ((arg4 == 0)) {
return ret;
}
ret = 0xffffffff;
if ((arg5 == 0)) {
return ret;
}
if (((unsigned long)((unsigned int)(arg3)) == 0)) {
return (unsigned int)(arg0);
}
var4 = (long)(arg1);
var5 = (unsigned long)((unsigned int)(arg3));
balance = (unsigned long)((unsigned int)(arg0));
period = 0;
while (1) {
interest = ((unsigned long)(((long)(balance) * var4)) >> 16);
var15 = ((unsigned int)(arg2) - interest);
var16 = (unsigned long)((unsigned int)(var15));
if (((long)((int)(var15)) < 0)) {
break;
}
var17 = (((((unsigned int)(var16) == (unsigned int)(balance)) | ((long)((int)(var16)) < (long)(balance))) == 0) ? balance : var16);
*(int *)(((long)arg4 + period * 4)) = interest;
*(int *)(((long)arg5 + period * 4)) = var17;
balance = (unsigned long)((unsigned int)((balance - var17)));
period = (period + 1);
ret = (unsigned long)((unsigned int)(balance));
if ((var5 == period)) {
return ret;
}
}
return 0xfffffffe;
} remaining_balance pass 59 lines
// glaurung: remaining_balance @ 0x1180
int32_t remaining_balance(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
int period;
int interest;
int reduction;
int balance;
long ret;
long var10;
int var15;
long var19;
long var4;
long var7;
ret = 0xffffffff;
if (((unsigned long)((unsigned long)((unsigned int)(arg3))) <= (unsigned long)(12))) {
ret = 0xffffffff;
if ((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0))) {
return ret;
}
ret = 0xffffffff;
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) == 0)) {
return ret;
}
ret = 0xffffffff;
if (((long)((int)(((unsigned long)((unsigned int)(arg1)) | arg0))) < 0)) {
return ret;
}
if (((unsigned long)((unsigned int)(arg3)) == 0)) {
return (unsigned int)(arg0);
}
if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
return (unsigned int)(arg0);
}
var4 = (long)(arg1);
var7 = 0;
period = 1;
var10 = (unsigned long)((unsigned int)(arg0));
while (1) {
interest = ((unsigned long)(((unsigned long)((unsigned int)(var10)) * var4)) >> 16);
var15 = ((unsigned int)(arg2) - interest);
reduction = (unsigned long)((unsigned int)(var15));
ret = var10;
if (((((unsigned long)((unsigned int)(var15)) == 0) | (arg2 < interest)) != 0)) {
break;
}
var19 = (((unsigned long)((unsigned long)((unsigned int)(var10))) < (unsigned long)((unsigned long)((unsigned int)(reduction)))) ? var7 : (unsigned long)((unsigned int)((var10 - reduction))));
ret = var19;
if ((arg3 <= period)) {
break;
}
period = (unsigned long)((unsigned int)((period + 1)));
var10 = var19;
ret = var19;
if (((((unsigned long)((unsigned int)(var19)) == 0) | ((long)((int)(var19)) < 0)) != 0)) {
break;
}
}
}
return ret;
} gcc -O0
2/2amortization_schedule pass 53 lines
// glaurung: amortization_schedule @ 0x111c
int32_t amortization_schedule(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t * arg4, int32_t * arg5) {
extern int loan_mul_q16(int, int);
int balance;
int period;
int interest;
int reduction;
int var3;
balance = arg0;
if ((arg4 != 0)) {
if ((arg5 != 0)) {
if ((0 <= (long)(arg0))) {
if ((0 <= (long)(arg1))) {
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) != 0)) {
if (((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0)) == 0)) {
if ((0 <= (long)(arg3))) {
if ((((unsigned long)((unsigned int)(arg3)) == 12) | ((long)(arg3) < 12))) {
goto L_1181;
}
}
}
}
}
}
}
}
// x86-64 epilogue: restore rbp
return 0xffffffff;
L_1181: ;
period = 0;
goto L_11fc;
L_118a: ;
var3 = loan_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
interest = var3;
reduction = ((unsigned int)(arg2) - interest);
if (((long)(reduction) < 0)) {
// x86-64 epilogue: restore rbp
return 0xfffffffe;
}
if (((((unsigned int)(reduction) == (unsigned int)(balance)) | (reduction < balance)) == 0)) {
reduction = balance;
}
arg4[(long)(period)] = interest;
arg5[(long)(period)] = reduction;
balance = (balance - (unsigned int)(reduction));
period = (period + 1);
L_11fc: ;
if ((period < arg3)) {
goto L_118a;
}
// x86-64 epilogue: restore rbp
return (unsigned int)(balance);
} remaining_balance pass 48 lines
// glaurung: remaining_balance @ 0x1209
int32_t remaining_balance(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
extern int loan_mul_q16(int, int);
int balance;
int period;
int interest;
int reduction;
int var3;
balance = arg0;
if ((0 <= (long)(arg0))) {
if ((0 <= (long)(arg1))) {
if (((((unsigned long)((unsigned int)(arg1)) == 0x10000) | ((long)(arg1) < 0x10000)) != 0)) {
if (((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0)) == 0)) {
if ((0 <= (long)(arg3))) {
if ((((unsigned long)((unsigned int)(arg3)) == 12) | ((long)(arg3) < 12))) {
goto L_1255;
}
}
}
}
}
}
// x86-64 epilogue: restore rbp
return 0xffffffff;
L_1255: ;
period = 0;
goto L_1296;
L_125e: ;
var3 = loan_mul_q16((unsigned long)((unsigned int)(balance)), (unsigned long)((unsigned int)(arg1)));
interest = var3;
reduction = ((unsigned int)(arg2) - interest);
if (((((unsigned long)((unsigned int)(reduction)) == 0) | ((long)(reduction) < 0)) != 0)) {
// x86-64 epilogue: restore rbp
return (unsigned int)(balance);
}
balance = (balance - ((((unsigned int)(reduction) == (unsigned int)(balance)) | (reduction < balance)) ? (unsigned long)((unsigned int)(reduction)) : (unsigned long)((unsigned int)(balance))));
period = (period + 1);
L_1296: ;
if ((arg3 <= period)) {
// x86-64 epilogue: restore rbp
return (unsigned int)(balance);
}
if (((((unsigned long)((unsigned int)(balance)) == 0) | ((long)(balance) < 0)) == 0)) {
goto L_125e;
}
// x86-64 epilogue: restore rbp
return (unsigned int)(balance);
} gcc -O2
2/2amortization_schedule pass 73 lines
// glaurung: amortization_schedule @ 0x1100
int32_t amortization_schedule(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3, int32_t * arg4, int32_t * arg5) {
int balance;
int interest;
int period;
int reduction;
long var0;
long var1;
long var11;
long var12;
long var13;
long var14;
int var15;
long var2;
long var20;
int var21;
long var6;
long var7;
int var8;
balance = (unsigned long)((unsigned int)(arg0));
var0 = (unsigned long)((unsigned int)(arg3));
var1 = (unsigned long)((unsigned int)(arg2));
if ((arg4 == 0)) {
return 0xffffffff;
}
if ((arg5 == 0)) {
return 0xffffffff;
}
if (((long)(arg0) < 0)) {
return 0xffffffff;
}
if ((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0))) {
return 0xffffffff;
}
if (((unsigned long)(0x10000) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return 0xffffffff;
}
if (((unsigned long)(12) < (unsigned long)((unsigned long)((unsigned int)(arg3))))) {
return 0xffffffff;
}
if (((unsigned long)((unsigned int)(arg3)) != 0)) {
var2 = (long)(arg1);
var6 = ((long)(((long)(balance) * (long)(arg1))) >> 16);
var7 = (unsigned long)((unsigned int)(var6));
var8 = ((unsigned int)(var1) - var6);
if (((long)((int)(var8)) < 0)) {
return 0xfffffffe;
}
var11 = (long)(((long)arg4 + ((unsigned long)((unsigned int)((var0 - 1))) * 4)));
var12 = (long)arg4;
var13 = (long)arg5;
var14 = (unsigned long)((unsigned int)(var8));
while (1) {
*(int *)((var12)) = var7;
var15 = ((((unsigned int)(balance) == (unsigned int)(var14)) | ((long)(balance) < (long)((int)(var14)))) ? balance : var14);
*(int *)((var13)) = var15;
balance = (unsigned long)((unsigned int)((balance - var15)));
if ((var12 == var11)) {
break;
}
var12 = (var12 + 4);
var13 = (var13 + 4);
var20 = ((long)(((long)(balance) * var2)) >> 16);
var7 = (unsigned long)((unsigned int)(var20));
var21 = ((unsigned int)(var1) - var20);
var14 = (unsigned long)((unsigned int)(var21));
if (((long)((int)(var21)) < 0)) {
return 0xfffffffe;
}
}
}
return balance;
} remaining_balance pass 54 lines
// glaurung: remaining_balance @ 0x1190
int32_t remaining_balance(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
int balance;
int period;
int reduction;
long var0;
long var1;
int var12;
long var19;
long var6;
balance = (unsigned long)((unsigned int)(arg0));
var0 = (unsigned long)((unsigned int)(arg2));
if (((long)(arg0) < 0)) {
return 0xffffffff;
}
if ((((unsigned long)((unsigned int)(arg2)) == 0) | ((long)(arg2) < 0))) {
return 0xffffffff;
}
if (((unsigned long)(0x10000) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
return 0xffffffff;
}
if (((unsigned long)(12) < (unsigned long)((unsigned long)((unsigned int)(arg3))))) {
return 0xffffffff;
}
if ((((unsigned long)((unsigned int)(arg3)) != 0) && (0 <= (long)(arg3)))) {
if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
return balance;
}
var1 = (long)(arg1);
var6 = (unsigned long)((unsigned int)((arg2 - ((long)(((long)(arg0) * (long)(arg1))) >> 16))));
if ((((unsigned long)((unsigned int)(var6)) == 0) | ((long)((int)(var6)) < 0))) {
return balance;
}
period = 0;
reduction = var6;
while (1) {
var12 = (period + 1);
period = (unsigned long)((unsigned int)(var12));
balance = (unsigned long)((unsigned int)((balance - ((((unsigned int)(balance) == (unsigned int)(reduction)) | ((long)(balance) < (long)(reduction))) ? balance : reduction))));
if (((((unsigned int)(arg3) == (unsigned int)(var12)) | (arg3 < var12)) != 0)) {
break;
}
if ((((unsigned long)((unsigned int)(balance)) == 0) | ((long)(balance) < 0))) {
return balance;
}
var19 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var0)) - ((long)(((long)(balance) * var1)) >> 16))));
reduction = var19;
if (((((unsigned long)((unsigned int)(var19)) == 0) | ((long)((int)(var19)) < 0)) != 0)) {
break;
}
}
}
return balance;
}