Fixture 67

elastic collision

C · 3 functions · 4 lanes · 11 of 12 function-lanes behave identically

One lane has a function that returns a different result after decompilation: clang-O2 (2/3).

One-dimensional elastic and inelastic collisions in Q16.16, plus a momentum conservation residual. All three read the same four parameters, so a mis-assigned argument register is caught by comparing their results.

tests/decompiler_fixtures/src/67_elastic_collision.c source
#include <stdint.h>

/* One-dimensional elastic and inelastic collisions in Q16.16, plus a momentum
 * conservation residual.  All three read the same four parameters, so a
 * mis-assigned argument register is caught by comparing their results. */

static int32_t collide_mul_q16(int32_t left, int32_t right) {
    return (int32_t)(((int64_t)left * (int64_t)right) >> 16);
}

static int32_t collide_div_q16(int32_t numerator, int32_t denominator) {
    if (denominator == 0) {
        return 0;
    }
    return (int32_t)(((int64_t)numerator << 16) / (int64_t)denominator);
}

__attribute__((noinline)) int32_t
elastic_velocity_a(int32_t mass_a, int32_t mass_b, int32_t velocity_a,
                   int32_t velocity_b) {
    int32_t total;
    int32_t term;
    if (mass_a <= 0 || mass_b <= 0) {
        return 0;
    }
    total = mass_a + mass_b;
    term = collide_mul_q16(mass_a - mass_b, velocity_a) +
           collide_mul_q16(2 * mass_b, velocity_b);
    return collide_div_q16(term, total);
}

__attribute__((noinline)) int32_t
inelastic_velocity(int32_t mass_a, int32_t mass_b, int32_t velocity_a,
                   int32_t velocity_b) {
    int32_t momentum;
    if (mass_a <= 0 || mass_b <= 0) {
        return 0;
    }
    momentum = collide_mul_q16(mass_a, velocity_a) +
               collide_mul_q16(mass_b, velocity_b);
    return collide_div_q16(momentum, mass_a + mass_b);
}

__attribute__((noinline)) int32_t
momentum_residual(int32_t mass_a, int32_t mass_b, int32_t velocity_a,
                  int32_t velocity_b) {
    int32_t before = collide_mul_q16(mass_a, velocity_a) +
                     collide_mul_q16(mass_b, velocity_b);
    int32_t after_a = elastic_velocity_a(mass_a, mass_b, velocity_a, velocity_b);
    int32_t after_b = elastic_velocity_a(mass_b, mass_a, velocity_b, velocity_a);
    int32_t after = collide_mul_q16(mass_a, after_a) +
                    collide_mul_q16(mass_b, after_b);
    return before - after;
}

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 -O2

2/3
elastic_velocity_a fail 19 lines
// glaurung: elastic_velocity_a @ 0x1110
int32_t elastic_velocity_a(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    int total;
    long ret;
    long var17;
    ret = 0;
    if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
        return ret;
    }
    if ((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0))) {
        return ret;
    }
    total = (unsigned long)((unsigned int)((arg1 + arg0)));
    var17 = ((long)((int)((((unsigned long)(((long)(arg3) * (long)((int)((arg1 + arg1))))) >> 16) + ((unsigned long)(((long)(arg2) * (long)((int)((arg0 - arg1))))) >> 16)))) << 16);
    if ((((unsigned long)(var17) >> 32) == 0)) {
        return ((unsigned int)(((((unsigned long long)(unsigned int)((unsigned long)((unsigned int)(0))) << 32) | (unsigned int)(var17)) / (unsigned int)(total))));
    }
    return ((long)((((__int128)(long)(((long)(var17) >> 63)) * (((__int128)1) << 64)) + (unsigned long)(var17)) / (long)(total)));
}
inelastic_velocity pass 19 lines
// glaurung: inelastic_velocity @ 0x1160
int32_t inelastic_velocity(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    long ret;
    long var12;
    long var14;
    ret = 0;
    if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
        return ret;
    }
    if ((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0))) {
        return ret;
    }
    var12 = (unsigned long)((unsigned int)((arg1 + arg0)));
    var14 = ((long)((int)((((unsigned long)(((long)(arg3) * (unsigned long)((unsigned int)(arg1)))) >> 16) + ((unsigned long)(((long)(arg2) * (unsigned long)((unsigned int)(arg0)))) >> 16)))) << 16);
    if ((((unsigned long)(var14) >> 32) == 0)) {
        return ((unsigned int)(((((unsigned long long)(unsigned int)((unsigned long)((unsigned int)(0))) << 32) | (unsigned int)(var14)) / (unsigned int)(var12))));
    }
    return ((long)((((__int128)(long)(((long)(var14) >> 63)) * (((__int128)1) << 64)) + (unsigned long)(var14)) / (long)(var12)));
}
momentum_residual pass 26 lines
// glaurung: momentum_residual @ 0x11b0
__attribute__((no_stack_protector)) int32_t momentum_residual(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int elastic_velocity_a(int, int, int, int);
    int after_b;
    unsigned char local_38[56];
    long var0;
    long var1;
    int var10;
    int var12;
    long var4;
    long var5;
    long var6;
    long var9;
    // x86-64 prologue: save callee registers, frame 56 bytes
    var0 = (long)(arg0);
    var1 = (long)(arg2);
    var4 = ((unsigned long)(((long)(arg2) * (long)(arg0))) >> 16);
    var5 = (long)(arg1);
    var6 = (long)(arg3);
    var9 = ((unsigned long)(((long)(arg3) * (long)(arg1))) >> 16);
    var10 = elastic_velocity_a((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)), (unsigned long)((unsigned int)(arg3)));
    *(int *)((&local_38[0] + 4)) = var10;
    var12 = elastic_velocity_a((unsigned long)((unsigned int)(var5)), (unsigned long)((unsigned int)(var0)), (unsigned long)((unsigned int)(var6)), (unsigned long)((unsigned int)(var1)));
    // x86-64 epilogue: restore callee registers
    return (unsigned int)(((unsigned long)((unsigned int)((var9 + var4))) - (unsigned long)((unsigned int)((((unsigned long)(((long)((int)(var12)) * var5)) >> 16) + ((unsigned long)(((long)((int)(*(int *)((&local_38[0] + 4)))) * var0)) >> 16))))));
}

clang -O0

3/3
elastic_velocity_a pass 30 lines
// glaurung: elastic_velocity_a @ 0x1110
int32_t elastic_velocity_a(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int collide_div_q16(int, int);
    extern int collide_mul_q16(int, int);
    int total;
    int term;
    int local_20;
    int local_4;
    int var15;
    int var5;
    int var9;
    // x86-64 prologue: save rbp, frame 32 bytes
    if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0)) != 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    total = ((unsigned int)(arg0) + arg1);
    var5 = collide_mul_q16((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - arg1))), (unsigned long)((unsigned int)(arg2)));
    local_20 = var5;
    var9 = collide_mul_q16((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) << 1))), (unsigned long)((unsigned int)(arg3)));
    term = ((unsigned int)(local_20) + (unsigned int)(var9));
    var15 = collide_div_q16((unsigned long)((unsigned int)(term)), (unsigned long)((unsigned int)(total)));
    local_4 = var15;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
inelastic_velocity pass 28 lines
// glaurung: inelastic_velocity @ 0x11f0
int32_t inelastic_velocity(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int collide_div_q16(int, int);
    extern int collide_mul_q16(int, int);
    int momentum;
    int local_1c;
    int local_4;
    int var0;
    int var10;
    int var2;
    // x86-64 prologue: save rbp, frame 32 bytes
    if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0)) != 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    var0 = collide_mul_q16((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg2)));
    local_1c = var0;
    var2 = collide_mul_q16((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg3)));
    momentum = ((unsigned int)(local_1c) + (unsigned int)(var2));
    var10 = collide_div_q16((unsigned long)((unsigned int)(momentum)), (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) + arg1))));
    local_4 = var10;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
momentum_residual pass 32 lines
// glaurung: momentum_residual @ 0x1270
int32_t momentum_residual(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int collide_mul_q16(int, int);
    extern int elastic_velocity_a(int, int, int, int);
    int before;
    int after_a;
    int after_b;
    int after;
    int local_24;
    int local_28;
    int var0;
    int var10;
    int var12;
    int var14;
    int var2;
    int var8;
    // x86-64 prologue: save rbp, frame 48 bytes
    var0 = collide_mul_q16((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg2)));
    local_28 = var0;
    var2 = collide_mul_q16((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg3)));
    before = ((unsigned int)(local_28) + (unsigned int)(var2));
    var8 = elastic_velocity_a((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)), (unsigned long)((unsigned int)(arg3)));
    after_a = var8;
    var10 = elastic_velocity_a((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg3)), (unsigned long)((unsigned int)(arg2)));
    after_b = var10;
    var12 = collide_mul_q16((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(after_a)));
    local_24 = var12;
    var14 = collide_mul_q16((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(after_b)));
    after = ((unsigned int)(local_24) + (unsigned int)(var14));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(before)) - after));
}

gcc -O0

3/3
elastic_velocity_a pass 28 lines
// glaurung: elastic_velocity_a @ 0x116d
int32_t elastic_velocity_a(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int collide_div_q16(int, int);
    extern int collide_mul_q16(int, int);
    int total;
    int term;
    int ret;
    long var11;
    int var15;
    int var9;
    // x86-64 prologue: save rbp, frame 8 bytes
    if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0)) != 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    total = ((unsigned int)(arg1) + (unsigned int)(arg0));
    var9 = collide_mul_q16((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) - arg1))), (unsigned long)((unsigned int)(arg2)));
    var11 = (unsigned long)((unsigned int)(var9));
    var15 = collide_mul_q16((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) + (unsigned long)((unsigned int)(arg1))))), (unsigned long)((unsigned int)(arg3)));
    term = (var15 + var11);
    ret = collide_div_q16((unsigned long)((unsigned int)(term)), (unsigned long)((unsigned int)(total)));
    // x86-64 epilogue: restore rbp
    return ret;
}
inelastic_velocity pass 26 lines
// glaurung: inelastic_velocity @ 0x11e4
int32_t inelastic_velocity(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int collide_div_q16(int, int);
    extern int collide_mul_q16(int, int);
    int momentum;
    int ret;
    int var3;
    long var5;
    int var8;
    // x86-64 prologue: save rbp, frame 8 bytes
    if ((((unsigned long)((unsigned int)(arg0)) == 0) | ((long)(arg0) < 0))) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0)) != 0)) {
        // x86-64 epilogue: restore rbp
        return 0;
    }
    var3 = collide_mul_q16((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg2)));
    var5 = (unsigned long)((unsigned int)(var3));
    var8 = collide_mul_q16((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg3)));
    momentum = (var8 + var5);
    ret = collide_div_q16((unsigned long)((unsigned int)(momentum)), (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) + (unsigned long)((unsigned int)(arg1))))));
    // x86-64 epilogue: restore rbp
    return ret;
}
momentum_residual pass 32 lines
// glaurung: momentum_residual @ 0x124f
int32_t momentum_residual(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int collide_mul_q16(int, int);
    extern int elastic_velocity_a(int, int, int, int);
    int before;
    int after_a;
    int after_b;
    int after;
    int var12;
    int var15;
    int var19;
    int var2;
    long var21;
    int var24;
    long var4;
    int var7;
    // x86-64 prologue: save rbp, frame 8 bytes
    var2 = collide_mul_q16((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg2)));
    var4 = (unsigned long)((unsigned int)(var2));
    var7 = collide_mul_q16((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg3)));
    before = (var7 + var4);
    var12 = elastic_velocity_a((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)), (unsigned long)((unsigned int)(arg3)));
    after_a = var12;
    var15 = elastic_velocity_a((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg3)), (unsigned long)((unsigned int)(arg2)));
    after_b = var15;
    var19 = collide_mul_q16((unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(after_a)));
    var21 = (unsigned long)((unsigned int)(var19));
    var24 = collide_mul_q16((unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(after_b)));
    after = (var24 + var21);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(before)) - after));
}

gcc -O2

3/3
elastic_velocity_a pass 14 lines
// glaurung: elastic_velocity_a @ 0x1120
int32_t elastic_velocity_a(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    int total;
    long var0;
    long var19;
    var0 = (long)(arg2);
    if ((((unsigned long)((unsigned int)(arg0)) != 0) && (0 <= (long)(arg0)))) {
        if (((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0)) == 0)) {
            var19 = ((long)((int)((((long)(((long)((int)((arg1 + arg1))) * (long)(arg3))) >> 16) + ((long)(((long)((int)(((unsigned long)((unsigned int)(arg0)) - arg1))) * var0)) >> 16)))) << 16);
            return ((long)((((__int128)(long)(((long)(var19) >> 63)) * (((__int128)1) << 64)) + (unsigned long)(var19)) / (long)((long)((int)((unsigned long)((unsigned int)((arg0 + arg1))))))));
        }
    }
    return 0;
}
inelastic_velocity pass 11 lines
// glaurung: inelastic_velocity @ 0x1170
int32_t inelastic_velocity(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    long var15;
    if ((((unsigned long)((unsigned int)(arg0)) != 0) && (0 <= (long)(arg0)))) {
        if (((((unsigned long)((unsigned int)(arg1)) == 0) | ((long)(arg1) < 0)) == 0)) {
            var15 = ((long)((int)((((long)(((long)(arg1) * (long)(arg3))) >> 16) + ((long)(((long)(arg2) * (long)(arg0))) >> 16)))) << 16);
            return ((long)((((__int128)(long)(((long)(var15) >> 63)) * (((__int128)1) << 64)) + (unsigned long)(var15)) / (long)((long)((int)((unsigned long)((unsigned int)((arg0 + arg1))))))));
        }
    }
    return 0;
}
momentum_residual pass 27 lines
// glaurung: momentum_residual @ 0x11c0
int32_t momentum_residual(int32_t arg0, int32_t arg1, int32_t arg2, int32_t arg3) {
    extern int elastic_velocity_a(int, int, int, int);
    int after_b;
    int before;
    int local_40;
    long var1;
    long var12;
    int var13;
    int var15;
    long var2;
    long var5;
    long var7;
    long var8;
    var1 = (long)(arg0);
    var2 = (long)(arg2);
    var5 = (long)(arg1);
    var7 = ((long)(((long)(arg2) * (long)(arg0))) >> 16);
    var8 = (long)(arg3);
    arg2 = (long)(arg2);
    var12 = (unsigned long)((unsigned int)((((long)(((long)(arg3) * (long)(arg1))) >> 16) + var7)));
    var13 = elastic_velocity_a(arg0, (unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(var2)), (unsigned long)((unsigned int)(arg3)));
    local_40 = var13;
    var15 = elastic_velocity_a((unsigned long)((unsigned int)(var5)), (unsigned long)((unsigned int)(var1)), (unsigned long)((unsigned int)(var8)), (unsigned long)((unsigned int)(arg2)));
    // x86-64 epilogue: tear down frame
    return (unsigned int)(((unsigned long)((unsigned int)(var12)) - (unsigned long)((unsigned int)((((long)(((long)((int)(var15)) * var5)) >> 16) + ((long)(((long)(local_40) * var1)) >> 16))))));
}

← 213 fixtures