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.

tests/decompiler_fixtures/src/72_loan_amortization.c source
#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/2
amortization_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/2
amortization_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/2
amortization_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/2
amortization_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;
}

← 213 fixtures