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.
/* 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 = ⋄
const Right *right = ⋄
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 = ⋄
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 = ⋄ 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/3cpp_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/3cpp_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/3cpp_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/3cpp_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;
}