Fixture 134

cpp virtual inheritance

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

4 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (0/3), gcc-O0 (0/3), clang-O2 (2/3), gcc-O2 (2/3).

Virtual inheritance: the shared base appears once, reached through a virtual base offset stored in the vtable rather than at a compile-time-fixed displacement. Member access on the shared base is therefore an extra indirect load, and construction order is determined by the most-derived class.

tests/decompiler_fixtures/src/134_cpp_virtual_inheritance.cpp source
/* 134_cpp_virtual_inheritance.cpp
 *
 * Virtual inheritance: the shared base appears once, reached through a virtual
 * base offset stored in the vtable rather than at a compile-time-fixed
 * displacement. Member access on the shared base is therefore an extra indirect
 * load, and construction order is determined by the most-derived class.
 */
#include <stdint.h>

namespace {

class Base {
public:
    int32_t value;
    Base() : value(1) {}
    virtual ~Base() {}
    virtual int32_t identify() const { return 1; }
};

class Left : public virtual Base {
public:
    int32_t left_mark;
    Left() : left_mark(2) {}
    int32_t identify() const override { return 2; }
};

class Right : public virtual Base {
public:
    int32_t right_mark;
    Right() : right_mark(3) {}
    int32_t identify() const override { return 3; }
};

class Diamond : public Left, public Right {
public:
    int32_t diamond_mark;
    Diamond() : diamond_mark(4) {}
    int32_t identify() const override { return 4; }
};

}  // namespace

extern "C" int32_t cpp_virtual_base_is_shared(int32_t seed) {
    Diamond diamond;
    diamond.value = seed;
    /* One shared Base: both paths observe the same object. */
    const Left *left = &diamond;
    const Right *right = &diamond;
    return (left->value == right->value) ? left->value : -1;
}

extern "C" int32_t cpp_virtual_base_offset(int32_t seed) {
    Diamond diamond;
    diamond.value = seed;
    diamond.left_mark = seed + 1;
    diamond.right_mark = seed + 2;
    diamond.diamond_mark = seed + 3;
    /* Reaching `value` from a Left* needs the virtual base offset from the
     * vtable; reaching `left_mark` does not. */
    const Left *left = &diamond;
    return left->value * 1000 + left->left_mark;
}

extern "C" int32_t cpp_virtual_final_overrider(int32_t which) {
    Diamond diamond;
    Left left;
    Right right;
    const Base *base;
    switch (which & 3) {
    case 0: base = &diamond; break;
    case 1: base = &left; break;
    default: base = &right; break;
    }
    return base->identify();
}

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

0/3
cpp_virtual_base_is_shared fail 39 lines
// glaurung: cpp_virtual_base_is_shared @ 0x1120
typedef struct Left Left;
typedef struct Right Right;
struct Diamond;
__attribute__((no_stack_protector)) int32_t cpp_virtual_base_is_shared(int32_t arg0) {
    extern void _ZN12_GLOBAL__N_17DiamondC1Ev(struct Diamond *);
    extern void _ZN12_GLOBAL__N_17DiamondD1Ev(struct Diamond *);
    unsigned char left[32];
    unsigned char right[32];
    unsigned char local_38[48];
    long local_50;
    int local_54;
    int local_58;
    long var10;
    long var14;
    long var18;
    // x86-64 prologue: save rbp, frame 96 bytes
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondC1Ev)((char *)(&local_38[0]));
    *(int *)((&local_38[0] + (*(long *)((*(long *)(&local_38[0]) - 24)) + 8))) = arg0;
    *(long *)(&left[0]) = (long)((long)(&local_38[0]));
    local_50 = 0;
    if ((&local_38[0] != 0)) {
        local_50 = (long)((&local_38[0] + 16));
    }
    *(long *)(&right[0]) = local_50;
    var10 = *(long *)(&left[0]);
    var14 = *(long *)(&right[0]);
    if (((unsigned int)(*(int *)(((var10 + *(long *)((*(long *)((var10)) - 24))) + 8))) != (unsigned int)(*(int *)(((var14 + *(long *)((*(long *)((var14)) - 24))) + 8))))) {
        local_54 = 0xffffffff;
    } else {
        var18 = *(long *)(&left[0]);
        var14 = *(long *)((*(long *)((var18)) - 24));
        local_54 = *(int *)((var18 + var14 + 0x8));
    }
    local_58 = local_54;
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_38[0]));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_58);
}
cpp_virtual_base_offset fail 23 lines
// glaurung: cpp_virtual_base_offset @ 0x1300
typedef struct Left Left;
struct Diamond;
__attribute__((no_stack_protector)) int32_t cpp_virtual_base_offset(int32_t arg0) {
    extern void _ZN12_GLOBAL__N_17DiamondC1Ev(struct Diamond *);
    extern void _ZN12_GLOBAL__N_17DiamondD1Ev(struct Diamond *);
    unsigned char left[32];
    unsigned char local_38[48];
    int local_44;
    long var13;
    // x86-64 prologue: save rbp, frame 80 bytes
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondC1Ev)((char *)(&local_38[0]));
    *(int *)((&local_38[0] + (*(long *)((*(long *)(&local_38[0]) - 24)) + 8))) = arg0;
    *(int *)((&local_38[0] + 8)) = ((unsigned long)((unsigned int)(arg0)) + 1);
    *(int *)((&local_38[0] + 24)) = ((unsigned long)((unsigned int)(arg0)) + 2);
    *(int *)((&local_38[0] + 28)) = ((unsigned long)((unsigned int)(arg0)) + 3);
    *(long *)(&left[0]) = (long)((long)(&local_38[0]));
    var13 = *(long *)(&left[0]);
    local_44 = ((*(int *)(((var13 + *(long *)((*(long *)((var13)) - 24))) + 8)) * 1000) + *(int *)((*(long *)(&left[0]) + 8)));
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_38[0]));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_44);
}
cpp_virtual_final_overrider fail 65 lines
// glaurung: cpp_virtual_final_overrider @ 0x1380
typedef struct Base Base;
struct Diamond;
struct Left;
struct Right;
__attribute__((no_stack_protector)) int32_t cpp_virtual_final_overrider(int32_t arg0) {
    extern void _ZN12_GLOBAL__N_14LeftC1Ev(struct Left *);
    extern void _ZN12_GLOBAL__N_14LeftD1Ev(struct Left *);
    extern void _ZN12_GLOBAL__N_15RightC1Ev(struct Right *);
    extern void _ZN12_GLOBAL__N_15RightD1Ev(struct Right *);
    extern void _ZN12_GLOBAL__N_17DiamondC1Ev(struct Diamond *);
    extern void _ZN12_GLOBAL__N_17DiamondD1Ev(struct Diamond *);
    unsigned char base[16];
    unsigned char local_38[48];
    unsigned char local_58[32];
    unsigned char local_88[32];
    int local_94;
    long local_a0;
    long local_a8;
    long local_b0;
    int local_b4;
    int var1;
    long var28;
    long var5;
    // x86-64 prologue: save rbp, frame 192 bytes
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondC1Ev)((char *)(&local_38[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_14LeftC1Ev)((char *)(&local_58[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_15RightC1Ev)((char *)(&local_88[0]));
    var1 = ((unsigned int)(arg0) & 3);
    local_94 = var1;
    if (((unsigned long)((unsigned int)(var1)) == 0)) {
        var5 = (long)(&local_38[0]);
        local_a0 = 0;
        if ((var5 != 0)) {
            var5 = *(long *)((*(long *)(&local_38[0]) - 24));
            local_a0 = (long)((&local_38[0] + var5));
        }
        *(long *)(&base[0]) = local_a0;
    } else {
        if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_94)) - 1))) == 0)) {
            var5 = (long)(&local_58[0]);
            local_a8 = 0;
            if ((var5 != 0)) {
                var5 = *(long *)((*(long *)(&local_58[0]) - 24));
                local_a8 = (long)((&local_58[0] + var5));
            }
            *(long *)(&base[0]) = local_a8;
        } else {
            var5 = (long)(&local_88[0]);
            local_b0 = 0;
            if ((var5 != 0)) {
                var5 = *(long *)((*(long *)(&local_88[0]) - 24));
                local_b0 = (long)((&local_88[0] + var5));
            }
            *(long *)(&base[0]) = local_b0;
        }
    }
    var28 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&base[0])) + 16))))();
    local_b4 = var28;
    ((void (*)(char *))_ZN12_GLOBAL__N_15RightD1Ev)((char *)(&local_88[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_14LeftD1Ev)((char *)(&local_58[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_38[0]));
    // x86-64 epilogue: restore rbp
    return (unsigned int)(local_b4);
}

gcc -O0

0/3
cpp_virtual_base_is_shared fail 36 lines
// glaurung: cpp_virtual_base_is_shared @ 0x1598
typedef struct Left Left;
typedef struct Right Right;
struct Diamond;
int32_t cpp_virtual_base_is_shared(int32_t arg0) {
    extern void _ZN12_GLOBAL__N_17DiamondC1Ev(const struct Diamond *);
    extern void _ZN12_GLOBAL__N_17DiamondD1Ev(const struct Diamond *);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    const Left * left;
    const Right * right;
    long local_18;
    unsigned char local_50[48];
    long ret;
    long var15;
    long var24;
    // x86-64 prologue: save rbp, frame 8 bytes
    local_18 = (long)(0x28);
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondC1Ev)((char *)(&local_50[0]));
    *(int *)((&local_50[0] + 40)) = arg0;
    left = (const Left *)&local_50[0];
    right = (const Right *)((&local_50[0] + 16));
    var15 = (unsigned long)((unsigned int)(*(int *)((((long)left + *(long *)((*(long *)((long)left) - 24))) + 8))));
    if (((unsigned int)(var15) != (unsigned int)(*(int *)((((long)right + *(long *)((*(long *)((long)right) - 24))) + 8))))) {
        var24 = 0xffffffff;
    } else {
        var15 = *(long *)((*(long *)((long)left) - 24));
        var24 = (unsigned long)((unsigned int)(*(int *)((((long)left + var15) + 8))));
    }
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_50[0]));
    ret = (unsigned long)((unsigned int)(var24));
    if ((local_18 != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}
cpp_virtual_base_offset fail 29 lines
// glaurung: cpp_virtual_base_offset @ 0x1661
typedef struct Left Left;
struct Diamond;
int32_t cpp_virtual_base_offset(int32_t arg0) {
    extern void _ZN12_GLOBAL__N_17DiamondC1Ev(const struct Diamond *);
    extern void _ZN12_GLOBAL__N_17DiamondD1Ev(const struct Diamond *);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    const Left * left;
    long local_18;
    unsigned char local_50[48];
    long ret;
    long var26;
    // x86-64 prologue: save rbp, frame 8 bytes
    local_18 = (long)(0x28);
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondC1Ev)((char *)(&local_50[0]));
    *(int *)((&local_50[0] + 40)) = arg0;
    *(int *)((&local_50[0] + 8)) = ((unsigned long)((unsigned int)(arg0)) + 1);
    *(int *)((&local_50[0] + 24)) = ((unsigned long)((unsigned int)(arg0)) + 2);
    *(int *)((&local_50[0] + 28)) = ((unsigned long)((unsigned int)(arg0)) + 3);
    left = (const Left *)&local_50[0];
    var26 = (unsigned long)((unsigned int)((((unsigned long)((unsigned int)(*(int *)((((long)left + *(long *)((*(long *)((long)left) - 24))) + 8)))) * 1000) + (unsigned long)((unsigned int)(*(int *)(((long)left + 0x8)))))));
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_50[0]));
    ret = (unsigned long)((unsigned int)(var26));
    if ((local_18 != 0x28)) {
        __stack_chk_fail();
    }
    // x86-64 epilogue: restore rbp
    return ret;
}
cpp_virtual_final_overrider fail 60 lines
// glaurung: cpp_virtual_final_overrider @ 0x1708
typedef struct Base Base;
struct Diamond;
struct Left;
struct Right;
int32_t cpp_virtual_final_overrider(int32_t arg0) {
    extern __attribute__((noreturn)) long _Unwind_Resume(long);
    extern void _ZN12_GLOBAL__N_14LeftC1Ev(const struct Left *);
    extern void _ZN12_GLOBAL__N_14LeftD1Ev(const struct Left *);
    extern void _ZN12_GLOBAL__N_15RightC1Ev(const struct Right *);
    extern void _ZN12_GLOBAL__N_15RightD1Ev(const struct Right *);
    extern void _ZN12_GLOBAL__N_17DiamondC1Ev(const struct Diamond *);
    extern void _ZN12_GLOBAL__N_17DiamondD1Ev(const struct Diamond *);
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    const Base * base;
    long local_18;
    unsigned char local_50[48];
    unsigned char local_70[32];
    unsigned char local_90[32];
    long ret;
    long var20;
    long var21;
    long var22;
    long var29;
    long var34;
    long var8;
    // x86-64 prologue: save rbp, frame 8 bytes
    local_18 = (long)(0x28);
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondC1Ev)((char *)(&local_50[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_14LeftC1Ev)((char *)(&local_90[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_15RightC1Ev)((char *)(&local_70[0]));
    var8 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3)));
    if (((unsigned long)((unsigned int)(var8)) == 0)) {
        base = (const Base *)((&local_50[0] + 32));
    } else {
        if (((unsigned long)((unsigned int)(var8)) == 1)) {
            base = (const Base *)((&local_90[0] + 16));
        } else {
            base = (const Base *)((&local_70[0] + 16));
        }
    }
    var20 = ((long (*)(char *))(*(long *)((*(long *)((long)base) + 16))))((char *)((long)base));
    var21 = var20;
    var22 = (unsigned long)((unsigned int)(var20));
    ((void (*)(char *))_ZN12_GLOBAL__N_15RightD1Ev)((char *)(&local_70[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_14LeftD1Ev)((char *)(&local_90[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_50[0]));
    ret = (unsigned long)((unsigned int)(var22));
    if ((local_18 == 0x28)) {
        // x86-64 epilogue: restore rbp
        return ret;
    }
    __stack_chk_fail();
    // __glaurung_eh_landing_17f7
    var29 = var21;
    ((void (*)(char *))_ZN12_GLOBAL__N_15RightD1Ev)((char *)(&local_70[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_14LeftD1Ev)((char *)(&local_90[0]));
    ((void (*)(char *))_ZN12_GLOBAL__N_17DiamondD1Ev)((char *)(&local_50[0]));
    var34 = _Unwind_Resume(var29);
}

clang -O2

2/3
cpp_virtual_base_is_shared pass 4 lines
// glaurung: cpp_virtual_base_is_shared @ 0x1110
int32_t cpp_virtual_base_is_shared(int32_t arg0) {
    return (unsigned int)(arg0);
}
cpp_virtual_base_offset pass 4 lines
// glaurung: cpp_virtual_base_offset @ 0x1130
int32_t cpp_virtual_base_offset(int32_t arg0) {
    return (unsigned int)(((unsigned long)((unsigned int)(((arg0 * 1000) + arg0))) + 1));
}
cpp_virtual_final_overrider fail 35 lines
// glaurung: cpp_virtual_final_overrider @ 0x1140
typedef struct Base Base;
__attribute__((no_stack_protector)) int32_t cpp_virtual_final_overrider(int32_t arg0) {
    const Base * base;
    unsigned char local_10[12];
    long local_18;
    long local_20;
    int local_28;
    long local_30;
    unsigned char local_40[12];
    int local_48;
    long local_50;
    unsigned char local_60[12];
    int local_68;
    long local_70;
    int ret;
    long var9;
    *(int *)((&local_10[0] + 8)) = 1;
    local_28 = 2;
    local_18 = 0x400000003;
    local_30 = 0x3c08;
    *(long *)(&local_10[0]) = 0x3c70;
    local_20 = 0x3c38;
    *(int *)((&local_40[0] + 8)) = 1;
    local_50 = 0x3d38;
    *(long *)(&local_40[0]) = 0x3d70;
    local_48 = 2;
    *(int *)((&local_60[0] + 8)) = 1;
    local_70 = 0x3da0;
    *(long *)(&local_60[0]) = 0x3dd8;
    local_68 = 3;
    var9 = (unsigned long)((unsigned int)((arg0 & 3)));
    ret = ((int (*)(void))(*(long *)((*(long *)((((unsigned long)((unsigned int)(var9)) == 1) ? &local_40[0] : (((unsigned long)((unsigned int)(var9)) != 0) ? &local_60[0] : &local_10[0]))) + 16))))();
    return ret;
}

gcc -O2

2/3
cpp_virtual_base_is_shared pass 4 lines
// glaurung: cpp_virtual_base_is_shared @ 0x12d0
int32_t cpp_virtual_base_is_shared(int32_t arg0) {
    return (unsigned int)(arg0);
}
cpp_virtual_base_offset pass 4 lines
// glaurung: cpp_virtual_base_offset @ 0x12e0
int32_t cpp_virtual_base_offset(int32_t arg0) {
    return (unsigned int)(((arg0 * 1001) + 1));
}
cpp_virtual_final_overrider fail 49 lines
// glaurung: cpp_virtual_final_overrider @ 0x12f0
typedef struct Base Base;
int32_t cpp_virtual_final_overrider(int32_t arg0) {
    extern __attribute__((noreturn)) void __stack_chk_fail(void);
    const Base * base;
    long local_10;
    unsigned char local_48[48];
    unsigned char local_68[32];
    unsigned char local_88[32];
    long ret;
    long var11;
    int var12;
    long var13;
    long var17;
    local_10 = (long)(0x28);
    *(int *)((&local_48[0] + 40)) = 1;
    *(long *)(&local_48[0]) = 0x3cf8;
    *(long *)((&local_48[0] + 32)) = 0x3d60;
    *(long *)((&local_48[0] + 16)) = 0x3d28;
    *(long *)(&local_88[0]) = 0x3c28;
    *(long *)((&local_88[0] + 16)) = 0x3c60;
    *(long *)(&local_68[0]) = 0x3c90;
    *(long *)((&local_68[0] + 16)) = 0x3cc8;
    var11 = (long)(0x1220);
    *(int *)((&local_48[0] + 8)) = 2;
    *(int *)((&local_48[0] + 24)) = 3;
    *(int *)((&local_48[0] + 28)) = 4;
    *(int *)((&local_88[0] + 24)) = 1;
    *(int *)((&local_88[0] + 8)) = 2;
    *(int *)((&local_68[0] + 24)) = 1;
    *(int *)((&local_68[0] + 8)) = 3;
    var12 = (arg0 & 3);
    var13 = (unsigned long)((unsigned int)(var12));
    base = (const Base *)((&local_48[0] + 32));
    if (((unsigned long)((unsigned int)(var12)) != 0)) {
        var11 = (long)(0x11f0);
        base = (const Base *)((&local_68[0] + 16));
        if (((unsigned long)((unsigned int)(var13)) == 1)) {
            var11 = (long)(0x11e0);
            base = (const Base *)((&local_88[0] + 16));
        }
    }
    var17 = ((long (*)(char *))(var11))((char *)((long)base));
    ret = var17;
    if ((local_10 != 0x28)) {
        __stack_chk_fail();
    }
    return ret;
}

← 213 fixtures