Fixture 138

cpp operators

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

One lane has a function that returns a different result after decompilation: gcc-O0 (0/3).

Operator overloading turns ordinary-looking syntax into calls. a + b on a class type is a function call with a hidden this or two by-value arguments; a conversion operator inserts a call where the source shows no call at all.

tests/decompiler_fixtures/src/138_cpp_operators.cpp source
/* 138_cpp_operators.cpp
 *
 * Operator overloading turns ordinary-looking syntax into calls. `a + b` on a
 * class type is a function call with a hidden `this` or two by-value arguments;
 * a conversion operator inserts a call where the source shows no call at all.
 */
#include <stdint.h>

namespace {

class Fixed {
public:
    int32_t raw;

    explicit Fixed(int32_t value) : raw(value) {}

    Fixed operator+(const Fixed &other) const {
        return Fixed(static_cast<int32_t>(
            static_cast<uint32_t>(raw) + static_cast<uint32_t>(other.raw)));
    }
    Fixed operator*(const Fixed &other) const {
        return Fixed(static_cast<int32_t>(
            (static_cast<int64_t>(raw) * static_cast<int64_t>(other.raw)) >> 16));
    }
    Fixed &operator+=(const Fixed &other) {
        raw = static_cast<int32_t>(static_cast<uint32_t>(raw) +
                                   static_cast<uint32_t>(other.raw));
        return *this;
    }
    bool operator<(const Fixed &other) const { return raw < other.raw; }
    int32_t operator[](int32_t index) const { return (raw >> (index & 15)) & 1; }
    /* An implicit conversion: appears as no call in the source. */
    operator int32_t() const { return raw >> 16; }
};

}  // namespace

extern "C" int32_t cpp_operator_arithmetic(int32_t a, int32_t b) {
    Fixed left(a);
    Fixed right(b);
    Fixed sum = left + right;
    Fixed product = left * right;
    return sum.raw ^ product.raw;
}

extern "C" int32_t cpp_operator_compound(int32_t seed, int32_t steps) {
    Fixed value(seed);
    Fixed step(1 << 16);
    if (steps < 0 || steps > 16) {
        return -1;
    }
    for (int32_t index = 0; index < steps; ++index) {
        value += step;
    }
    return value.raw;
}

extern "C" int32_t cpp_operator_conversion(int32_t a, int32_t b) {
    Fixed left(a);
    Fixed right(b);
    /* Both operands convert to int32_t through the conversion operator. */
    int32_t widened = left + 0;
    int32_t compared = (left < right) ? 1 : 0;
    return widened * 10 + compared + left[3];
}

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.

gcc -O0

0/3
cpp_operator_arithmetic fail 29 lines
// glaurung: cpp_operator_arithmetic @ 0x126b
int32_t cpp_operator_arithmetic(int32_t arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_15FixedC2Ei(char *, int);
    extern unsigned int _ZNK12_GLOBAL__N_15FixedmlERKS0_(char *, int *);
    extern unsigned int _ZNK12_GLOBAL__N_15FixedplERKS0_(char *, int *);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    unsigned char local_10[4];
    unsigned char local_14[4];
    unsigned char local_18[4];
    long local_8;
    unsigned char local_c[4];
    long ret;
    unsigned int var13;
    unsigned int var9;
    // x86-64 prologue: save rbp, frame 48 bytes
    local_8 = (long)(0x28);
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_18[0]), (unsigned long)((unsigned int)(arg0)));
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_14[0]), (unsigned long)((unsigned int)(arg1)));
    var9 = _ZNK12_GLOBAL__N_15FixedplERKS0_((char *)(&local_18[0]), (int *)(&local_14[0]));
    *(int *)(&local_10[0]) = var9;
    var13 = _ZNK12_GLOBAL__N_15FixedmlERKS0_((char *)(&local_18[0]), (int *)(&local_14[0]));
    *(int *)(&local_c[0]) = var13;
    ret = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_c[0]))) ^ (unsigned long)((unsigned int)(*(int *)(&local_10[0]))))));
    if ((local_8 != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}
cpp_operator_compound fail 29 lines
// glaurung: cpp_operator_compound @ 0x12f8
int32_t cpp_operator_compound(int32_t arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_15FixedC2Ei(char *, int);
    extern long _ZN12_GLOBAL__N_15FixedpLERKS0_(char *, int *);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int index;
    unsigned char local_10[4];
    unsigned char local_14[4];
    long local_8;
    long ret;
    long var9;
    // x86-64 prologue: save rbp, frame 48 bytes
    local_8 = (long)(0x28);
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_14[0]), (unsigned long)((unsigned int)(arg0)));
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_10[0]), 0x10000);
    if ((((long)(arg1) < 0) || (((unsigned long)((unsigned int)(arg1)) != 16) && (16 <= (long)(arg1))))) {
        ret = 0xffffffff;
    } else {
        for (index = 0; (index < arg1); index++) {
            var9 = _ZN12_GLOBAL__N_15FixedpLERKS0_((char *)(&local_14[0]), (int *)(&local_10[0]));
        }
        ret = (unsigned long)((unsigned int)(*(int *)(&local_14[0])));
    }
    if ((local_8 != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}
cpp_operator_conversion fail 36 lines
// glaurung: cpp_operator_conversion @ 0x138f
int32_t cpp_operator_conversion(int32_t arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_15FixedC2Ei(char *, int);
    extern int _ZNK12_GLOBAL__N_15FixedcviEv(char *);
    extern int _ZNK12_GLOBAL__N_15FixedixEi(char *, int);
    extern int _ZNK12_GLOBAL__N_15FixedltERKS0_(char *, int *);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    int widened;
    int compared;
    long local_18;
    unsigned char local_24[4];
    unsigned char local_28[4];
    long ret;
    int var12;
    int var19;
    long var25;
    int var27;
    int var8;
    // x86-64 prologue: save rbp, frame 8 bytes
    local_18 = (long)(0x28);
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_28[0]), (unsigned long)((unsigned int)(arg0)));
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_24[0]), (unsigned long)((unsigned int)(arg1)));
    var8 = _ZNK12_GLOBAL__N_15FixedcviEv((char *)(&local_28[0]));
    widened = var8;
    var12 = _ZNK12_GLOBAL__N_15FixedltERKS0_((char *)(&local_28[0]), (int *)(&local_24[0]));
    compared = (((unsigned long)((unsigned char)((var12 & 255))) == 0) ? 0 : 1);
    var19 = ((unsigned int)(((unsigned long)((unsigned int)(widened)) << 2)) + (unsigned int)(widened));
    var25 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var19)) + (unsigned long)((unsigned int)(var19))))) + (unsigned long)((unsigned int)(compared)))));
    var27 = _ZNK12_GLOBAL__N_15FixedixEi((char *)(&local_28[0]), 3);
    ret = (unsigned long)((unsigned int)((var27 + var25)));
    if ((local_18 != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}

clang -O0

3/3
cpp_operator_arithmetic pass 21 lines
// glaurung: cpp_operator_arithmetic @ 0x1100
__attribute__((no_stack_protector)) int32_t cpp_operator_arithmetic(int32_t arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_15FixedC2Ei(char *, int);
    extern unsigned int _ZNK12_GLOBAL__N_15FixedmlERKS0_(char *, int *);
    extern unsigned int _ZNK12_GLOBAL__N_15FixedplERKS0_(char *, int *);
    unsigned char local_10[4];
    unsigned char local_18[4];
    unsigned char local_20[4];
    unsigned char local_28[4];
    unsigned int var0;
    unsigned int var2;
    // x86-64 prologue: save rbp, frame 48 bytes
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_10[0]), (unsigned long)((unsigned int)(arg0)));
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_18[0]), (unsigned long)((unsigned int)(arg1)));
    var0 = _ZNK12_GLOBAL__N_15FixedplERKS0_((char *)(&local_10[0]), (int *)(&local_18[0]));
    *(int *)(&local_20[0]) = var0;
    var2 = _ZNK12_GLOBAL__N_15FixedmlERKS0_((char *)(&local_10[0]), (int *)(&local_18[0]));
    *(int *)(&local_28[0]) = var2;
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(*(int *)(&local_20[0]))) ^ *(int *)(&local_28[0])));
}
cpp_operator_compound pass 29 lines
// glaurung: cpp_operator_compound @ 0x11f0
__attribute__((no_stack_protector)) int32_t cpp_operator_compound(int32_t arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_15FixedC2Ei(char *, int);
    extern long _ZN12_GLOBAL__N_15FixedpLERKS0_(char *, int *);
    int index;
    unsigned char local_10[4];
    unsigned char local_18[4];
    int local_4;
    long var1;
    // x86-64 prologue: save rbp, frame 32 bytes
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_10[0]), (unsigned long)((unsigned int)(arg0)));
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_18[0]), 0x10000);
    if (((long)(arg1) < 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    if (((((unsigned long)((unsigned int)(arg1)) == 16) | ((long)(arg1) < 16)) == 0)) {
        local_4 = -1;
        // x86-64 epilogue: restore rbp
        return (unsigned int)(local_4);
    }
    for (index = 0; (index < arg1); index++) {
        var1 = _ZN12_GLOBAL__N_15FixedpLERKS0_((char *)(&local_10[0]), (int *)(&local_18[0]));
    }
    local_4 = *(int *)(&local_10[0]);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_4);
}
cpp_operator_conversion pass 25 lines
// glaurung: cpp_operator_conversion @ 0x12a0
__attribute__((no_stack_protector)) int32_t cpp_operator_conversion(int32_t arg0, int32_t arg1) {
    extern void _ZN12_GLOBAL__N_15FixedC2Ei(char *, int);
    extern int _ZNK12_GLOBAL__N_15FixedcviEv(char *);
    extern int _ZNK12_GLOBAL__N_15FixedixEi(char *, int);
    extern int _ZNK12_GLOBAL__N_15FixedltERKS0_(char *, int *);
    int widened;
    int compared;
    unsigned char local_10[4];
    unsigned char local_18[4];
    int local_24;
    int var0;
    int var16;
    int var4;
    // x86-64 prologue: save rbp, frame 48 bytes
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_10[0]), (unsigned long)((unsigned int)(arg0)));
    _ZN12_GLOBAL__N_15FixedC2Ei((char *)(&local_18[0]), (unsigned long)((unsigned int)(arg1)));
    var0 = _ZNK12_GLOBAL__N_15FixedcviEv((char *)(&local_10[0]));
    widened = var0;
    var4 = _ZNK12_GLOBAL__N_15FixedltERKS0_((char *)(&local_10[0]), (int *)(&local_18[0]));
    local_24 = ((widened * 10) + (((unsigned long)((unsigned char)((var4 & 1))) != 0) ? 1 : 0));
    var16 = _ZNK12_GLOBAL__N_15FixedixEi((char *)(&local_10[0]), 3);
    // x86-64 epilogue: restore rbp
    return (unsigned int)(((unsigned long)((unsigned int)(local_24)) + (unsigned long)((unsigned int)(var16))));
}

clang -O2

3/3
cpp_operator_arithmetic pass 4 lines
// glaurung: cpp_operator_arithmetic @ 0x1100
int32_t cpp_operator_arithmetic(int32_t arg0, int32_t arg1) {
    return (unsigned int)((((unsigned long)(((long)(arg1) * (long)(arg0))) >> 16) ^ (unsigned long)((unsigned int)((arg1 + arg0)))));
}
cpp_operator_compound pass 13 lines
// glaurung: cpp_operator_compound @ 0x1120
int32_t cpp_operator_compound(int32_t arg0, int32_t arg1) {
    int index;
    long ret;
    ret = 0xffffffff;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return ret;
    }
    if (((unsigned long)((unsigned int)(arg1)) == 0)) {
        return (unsigned int)(arg0);
    }
    return (unsigned int)(((unsigned long)((unsigned int)((arg1 << 16))) + arg0));
}
cpp_operator_conversion pass 8 lines
// glaurung: cpp_operator_conversion @ 0x1140
int32_t cpp_operator_conversion(int32_t arg0, int32_t arg1) {
    int widened;
    long var3;
    var3 = (unsigned long)((unsigned int)(((int)(arg0) >> 16)));
    widened = (var3 + var3);
    return (unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(widened)) + ((unsigned long)((unsigned int)(widened)) * 4)))) | (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) >> 3))) & 1)))))) + (arg0 < arg1)));
}

gcc -O2

3/3
cpp_operator_arithmetic pass 4 lines
// glaurung: cpp_operator_arithmetic @ 0x1100
int32_t cpp_operator_arithmetic(int32_t arg0, int32_t arg1) {
    return (unsigned int)(((unsigned long)((unsigned int)((arg0 + arg1))) ^ ((long)(((long)(arg0) * (long)(arg1))) >> 16)));
}
cpp_operator_compound pass 8 lines
// glaurung: cpp_operator_compound @ 0x1120
int32_t cpp_operator_compound(int32_t arg0, int32_t arg1) {
    int index;
    if (((unsigned long)(16) < (unsigned long)((unsigned long)((unsigned int)(arg1))))) {
        return 0xffffffff;
    }
    return (((unsigned long)((unsigned int)(arg1)) == 0) ? arg0 : (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg1)) << 16))) + arg0))));
}
cpp_operator_conversion pass 7 lines
// glaurung: cpp_operator_conversion @ 0x1140
int32_t cpp_operator_conversion(int32_t arg0, int32_t arg1) {
    int widened;
    long var3;
    var3 = (unsigned long)((unsigned int)(((int)(arg0) >> 16)));
    return (unsigned int)(((unsigned long)((unsigned int)(((arg0 < arg1) + ((unsigned long)((unsigned int)((var3 + (var3 * 4)))) * 2)))) + (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(((int)(arg0) >> 3))) & 1)))));
}

← 213 fixtures