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.

tests/decompiler_fixtures/src/71_compound_interest.c source
#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/2
annuity_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/2
annuity_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/2
annuity_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/2
annuity_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;
}

← 213 fixtures