Fixture 132
cpp vtable layout
C++ · 3 functions · 4 lanes · 3 of 12 function-lanes behave identically
4 of 4 lanes have a function that returns a different result after decompilation: clang-O0 (0/3), clang-O2 (1/3), gcc-O0 (1/3), gcc-O2 (1/3).
Single-inheritance virtual dispatch. Each polymorphic class gets a vtable in read-only data; an object's first word points at it, and a virtual call is a load of that pointer followed by an indirect call through a fixed slot index. Overriding replaces the slot, so the same call site reaches different bodies with no branch anywhere in the caller.
No STL: the target is the generated dispatch machinery, not library code.
/* 132_cpp_vtable_layout.cpp
*
* Single-inheritance virtual dispatch. Each polymorphic class gets a vtable in
* read-only data; an object's first word points at it, and a virtual call is a
* load of that pointer followed by an indirect call through a fixed slot index.
* Overriding replaces the slot, so the same call site reaches different bodies
* with no branch anywhere in the caller.
*
* No STL: the target is the generated dispatch machinery, not library code.
*/
#include <stdint.h>
namespace {
class Shape {
public:
virtual ~Shape() {}
virtual int32_t area(int32_t a, int32_t b) const = 0;
virtual int32_t perimeter(int32_t a, int32_t b) const {
return 2 * (a + b);
}
virtual int32_t tag() const { return 0; }
};
class Rect : public Shape {
public:
int32_t area(int32_t a, int32_t b) const override { return a * b; }
int32_t tag() const override { return 1; }
};
class Square : public Rect {
public:
int32_t area(int32_t a, int32_t) const override { return a * a; }
/* perimeter() is NOT overridden: the slot still points at Shape's body. */
int32_t tag() const override { return 2; }
};
class Triangle : public Shape {
public:
int32_t area(int32_t a, int32_t b) const override { return (a * b) / 2; }
int32_t perimeter(int32_t a, int32_t b) const override { return a + b + a; }
int32_t tag() const override { return 3; }
};
} // namespace
extern "C" int32_t cpp_vtable_area(int32_t which, int32_t a, int32_t b) {
Rect rect;
Square square;
Triangle triangle;
const Shape *shape;
switch (which & 3) {
case 0: shape = ▭ break;
case 1: shape = □ break;
case 2: shape = ▵ break;
default: shape = ▭ break;
}
return shape->area(a, b);
}
extern "C" int32_t cpp_vtable_inherited_slot(int32_t which, int32_t a,
int32_t b) {
Rect rect;
Square square;
Triangle triangle;
const Shape *shape;
switch (which & 3) {
case 0: shape = ▭ break;
case 1: shape = □ break;
default: shape = ▵ break;
}
/* Square inherits Shape::perimeter through Rect without overriding it. */
return shape->perimeter(a, b) * 10 + shape->tag();
}
extern "C" int32_t cpp_vtable_devirtualized(int32_t a, int32_t b) {
Square square;
/* The dynamic type is known here, so the call may be devirtualized and
* inlined at -O2 while remaining an indirect call at -O0. */
return square.area(a, b) + square.perimeter(a, b);
} 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_vtable_area fail 47 lines
// glaurung: cpp_vtable_area @ 0x1120
typedef struct Shape Shape;
struct Rect;
struct Square;
struct Triangle;
__attribute__((no_stack_protector)) int32_t cpp_vtable_area(int32_t arg0, int32_t arg1, int32_t arg2) {
extern void _ZN12_GLOBAL__N_14RectC2Ev(struct Rect *);
extern void _ZN12_GLOBAL__N_14RectD2Ev(struct Rect *);
extern void _ZN12_GLOBAL__N_16SquareC2Ev(struct Square *);
extern void _ZN12_GLOBAL__N_16SquareD2Ev(struct Square *);
extern void _ZN12_GLOBAL__N_18TriangleC2Ev(struct Triangle *);
extern void _ZN12_GLOBAL__N_18TriangleD2Ev(struct Triangle *);
unsigned char shape[8];
unsigned char local_18[8];
unsigned char local_20[8];
unsigned char local_28[8];
int local_40;
int local_44;
int var1;
long var18;
// x86-64 prologue: save rbp, frame 80 bytes
((void (*)(char *))_ZN12_GLOBAL__N_14RectC2Ev)((char *)(&local_18[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareC2Ev)((char *)(&local_20[0]));
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleC2Ev)((char *)(&local_28[0]));
var1 = ((unsigned int)(arg0) & 3);
local_40 = var1;
if (((unsigned long)((unsigned int)(var1)) == 0)) {
*(long *)(&shape[0]) = (long)((long)(&local_18[0]));
} else {
if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_40)) - 1))) == 0)) {
*(long *)(&shape[0]) = (long)((long)(&local_20[0]));
} else {
if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_40)) - 2))) == 0)) {
*(long *)(&shape[0]) = (long)((long)(&local_28[0]));
} else {
*(long *)(&shape[0]) = (long)((long)(&local_18[0]));
}
}
}
var18 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&shape[0])) + 16))))();
local_44 = var18;
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleD2Ev)((char *)(&local_28[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD2Ev)((char *)(&local_20[0]));
((void (*)(char *))_ZN12_GLOBAL__N_14RectD2Ev)((char *)(&local_18[0]));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_44);
} cpp_vtable_devirtualized fail 26 lines
// glaurung: cpp_vtable_devirtualized @ 0x1450
struct Square;
__attribute__((no_stack_protector)) int32_t cpp_vtable_devirtualized(int32_t arg0, int32_t arg1) {
extern void _ZN12_GLOBAL__N_16SquareC2Ev(struct Square *);
extern void _ZN12_GLOBAL__N_16SquareD2Ev(struct Square *);
extern int _ZNK12_GLOBAL__N_15Shape9perimeterEii(char *, int, int);
extern int _ZNK12_GLOBAL__N_16Square4areaEii(char *, int, int);
unsigned char local_10[8];
int local_20;
char * local_28;
int local_2c;
int local_30;
int var1;
int var3;
// x86-64 prologue: save rbp, frame 48 bytes
local_28 = (char *)&local_10[0];
((void (*)(void))_ZN12_GLOBAL__N_16SquareC2Ev)();
var1 = _ZNK12_GLOBAL__N_16Square4areaEii(local_28, (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
local_20 = var1;
var3 = _ZNK12_GLOBAL__N_15Shape9perimeterEii((char *)(&local_10[0]), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
local_2c = var3;
local_30 = ((unsigned int)(local_20) + (unsigned int)(local_2c));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD2Ev)((char *)(&local_10[0]));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_30);
} cpp_vtable_inherited_slot fail 53 lines
// glaurung: cpp_vtable_inherited_slot @ 0x1340
typedef struct Shape Shape;
struct Rect;
struct Square;
struct Triangle;
__attribute__((no_stack_protector)) int32_t cpp_vtable_inherited_slot(int32_t arg0, int32_t arg1, int32_t arg2) {
extern void _ZN12_GLOBAL__N_14RectC2Ev(struct Rect *);
extern void _ZN12_GLOBAL__N_14RectD2Ev(struct Rect *);
extern void _ZN12_GLOBAL__N_16SquareC2Ev(struct Square *);
extern void _ZN12_GLOBAL__N_16SquareD2Ev(struct Square *);
extern void _ZN12_GLOBAL__N_18TriangleC2Ev(struct Triangle *);
extern void _ZN12_GLOBAL__N_18TriangleD2Ev(struct Triangle *);
unsigned char shape[8];
unsigned char local_18[8];
unsigned char local_20[8];
unsigned char local_28[8];
int local_40;
int local_44;
int local_48;
int local_4c;
int local_50;
int var1;
long var14;
int var17;
long var24;
// x86-64 prologue: save rbp, frame 80 bytes
((void (*)(char *))_ZN12_GLOBAL__N_14RectC2Ev)((char *)(&local_18[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareC2Ev)((char *)(&local_20[0]));
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleC2Ev)((char *)(&local_28[0]));
var1 = ((unsigned int)(arg0) & 3);
local_40 = var1;
if (((unsigned long)((unsigned int)(var1)) == 0)) {
*(long *)(&shape[0]) = (long)((long)(&local_18[0]));
} else {
if (((unsigned long)((unsigned int)(((unsigned long)((unsigned int)(local_40)) - 1))) == 0)) {
*(long *)(&shape[0]) = (long)((long)(&local_20[0]));
} else {
*(long *)(&shape[0]) = (long)((long)(&local_28[0]));
}
}
var14 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&shape[0])) + 24))))();
local_44 = var14;
var17 = ((unsigned int)(local_44) + (unsigned int)(local_44));
local_4c = ((unsigned int)(var17) + ((unsigned int)(var17) * 4));
var24 = ((long (*)(void))(*(long *)((*(long *)(*(long *)(&shape[0])) + 32))))();
local_48 = var24;
local_50 = ((unsigned int)(local_4c) + (unsigned int)(local_48));
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleD2Ev)((char *)(&local_28[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD2Ev)((char *)(&local_20[0]));
((void (*)(char *))_ZN12_GLOBAL__N_14RectD2Ev)((char *)(&local_18[0]));
// x86-64 epilogue: restore rbp
return (unsigned int)(local_50);
} clang -O2
1/3cpp_vtable_area fail 18 lines
// glaurung: cpp_vtable_area @ 0x1110
typedef struct Shape Shape;
__attribute__((no_stack_protector)) int32_t cpp_vtable_area(int32_t arg0, int32_t arg1, int32_t arg2) {
const Shape * shape;
unsigned char local_10[16];
long local_18;
int ret;
long rsp;
long var4;
rsp = (rsp - 24);
local_18 = 0x3d00;
*(long *)((&local_10[0] + 8)) = 0x3d60;
*(long *)(&local_10[0]) = 0x3db0;
var4 = (unsigned long)((unsigned int)((arg0 & 3)));
ret = ((int (*)(void))(*(long *)((*(long *)((((unsigned long)((unsigned int)(var4)) == 2) ? &local_10[0] : (((unsigned long)((unsigned int)(var4)) == 1) ? (long)((&local_10[0] + 8)) : rsp))) + 16))))();
// x86-64 epilogue: tear down frame
return ret;
} cpp_vtable_devirtualized pass 4 lines
// glaurung: cpp_vtable_devirtualized @ 0x11f0
int32_t cpp_vtable_devirtualized(int32_t arg0, int32_t arg1) {
return (unsigned int)(((unsigned long)((unsigned int)((arg0 * arg0))) + ((unsigned long)((unsigned int)((arg1 + arg0))) * 2)));
} cpp_vtable_inherited_slot fail 24 lines
// glaurung: cpp_vtable_inherited_slot @ 0x1180
typedef struct Shape Shape;
__attribute__((no_stack_protector)) int32_t cpp_vtable_inherited_slot(int32_t arg0, int32_t arg1, int32_t arg2) {
const Shape * shape;
unsigned char local_20[32];
long local_28;
long rsp;
long var10;
long var12;
long var14;
long var4;
// x86-64 prologue: save callee registers, frame 16 bytes
rsp = (rsp - 24);
*(long *)((&local_20[0] + 8)) = 0x3d00;
*(long *)(&local_20[0]) = 0x3d60;
local_28 = 0x3db0;
var4 = (unsigned long)((unsigned int)((arg0 & 3)));
shape = (((unsigned long)((unsigned int)(var4)) == 1) ? (const Shape *)&local_20[0] : (((unsigned long)((unsigned int)(var4)) != 0) ? (const Shape *)(rsp) : (const Shape *)((&local_20[0] + 8))));
var10 = ((long (*)(char *))(*(long *)((*(long *)(((long)shape)) + 24))))((char *)((long)shape));
var12 = (unsigned long)((unsigned int)(var10));
var14 = ((long (*)(char *))(*(long *)((*(long *)(((long)shape)) + 32))))((char *)((long)shape));
// x86-64 epilogue: restore callee registers
return (unsigned int)((var14 + ((unsigned long)((unsigned int)((var12 + (var12 * 4)))) * 2)));
} gcc -O0
1/3cpp_vtable_area fail 66 lines
// glaurung: cpp_vtable_area @ 0x12bb
typedef struct Shape Shape;
struct Rect;
struct Square;
struct Triangle;
int32_t cpp_vtable_area(int32_t arg0, int32_t arg1, int32_t arg2) {
extern __attribute__((noreturn)) long _Unwind_Resume(long);
extern void _ZN12_GLOBAL__N_14RectD2Ev(const struct Rect *);
extern void _ZN12_GLOBAL__N_16SquareD1Ev(const struct Square *);
extern void _ZN12_GLOBAL__N_18TriangleD1Ev(const struct Triangle *);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
const Shape * shape;
long local_18;
unsigned char local_28[8];
unsigned char local_30[8];
unsigned char local_38[8];
long ret;
long var19;
long var20;
long var21;
long var28;
long var33;
long var8;
// x86-64 prologue: save rbp, frame 8 bytes
local_18 = (long)(0x28);
*(long *)(&local_38[0]) = 0x3d48;
*(long *)(&local_30[0]) = 0x3d10;
*(long *)(&local_28[0]) = 0x3cd8;
var8 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3)));
if (((unsigned long)((unsigned int)(var8)) == 2)) {
shape = (const Shape *)&local_28[0];
} else {
if (((((unsigned long)((unsigned int)(var8)) == 2) | ((long)((int)(var8)) < 2)) == 0)) {
L_133a: ;
shape = (const Shape *)&local_38[0];
} else {
if (((unsigned long)((unsigned int)(var8)) == 0)) {
shape = (const Shape *)&local_38[0];
} else {
if (((unsigned long)((unsigned int)(var8)) == 1)) {
shape = (const Shape *)&local_30[0];
} else {
goto L_133a;
}
}
}
}
var19 = ((long (*)(char *, long, long))(*(long *)((*(long *)((long)shape) + 16))))((char *)((long)shape), (unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
var20 = var19;
var21 = (unsigned long)((unsigned int)(var19));
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleD1Ev)((char *)(&local_28[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD1Ev)((char *)(&local_30[0]));
((void (*)(char *))_ZN12_GLOBAL__N_14RectD2Ev)((char *)(&local_38[0]));
ret = (unsigned long)((unsigned int)(var21));
if ((local_18 == 0x28)) {
// x86-64 epilogue: restore rbp
return ret;
}
__stack_chk_fail();
// __glaurung_eh_landing_139d
var28 = var20;
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleD1Ev)((char *)(&local_28[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD1Ev)((char *)(&local_30[0]));
((void (*)(char *))_ZN12_GLOBAL__N_14RectD2Ev)((char *)(&local_38[0]));
var33 = _Unwind_Resume(var28);
} cpp_vtable_devirtualized pass 29 lines
// glaurung: cpp_vtable_devirtualized @ 0x1510
struct Square;
int32_t cpp_vtable_devirtualized(int32_t arg0, int32_t arg1) {
extern void _ZN12_GLOBAL__N_16SquareD1Ev(const struct Square *);
extern int _ZNK12_GLOBAL__N_15Shape9perimeterEii(char *, int, int);
extern int _ZNK12_GLOBAL__N_16Square4areaEii(char *, int, int);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
long local_18;
unsigned char local_20[8];
long ret;
int var11;
long var14;
int var6;
long var8;
// x86-64 prologue: save rbp, frame 8 bytes
local_18 = (long)(0x28);
*(long *)(&local_20[0]) = 0x3d10;
var6 = _ZNK12_GLOBAL__N_16Square4areaEii((char *)(&local_20[0]), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
var8 = (unsigned long)((unsigned int)(var6));
var11 = _ZNK12_GLOBAL__N_15Shape9perimeterEii((char *)(&local_20[0]), (unsigned long)((unsigned int)(arg0)), (unsigned long)((unsigned int)(arg1)));
var14 = (unsigned long)((unsigned int)((var8 + var11)));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD1Ev)((char *)(&local_20[0]));
ret = (unsigned long)((unsigned int)(var14));
if ((local_18 != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
} cpp_vtable_inherited_slot fail 63 lines
// glaurung: cpp_vtable_inherited_slot @ 0x13de
typedef struct Shape Shape;
struct Rect;
struct Square;
struct Triangle;
int32_t cpp_vtable_inherited_slot(int32_t arg0, int32_t arg1, int32_t arg2) {
extern __attribute__((noreturn)) long _Unwind_Resume(long);
extern void _ZN12_GLOBAL__N_14RectD2Ev(const struct Rect *);
extern void _ZN12_GLOBAL__N_16SquareD1Ev(const struct Square *);
extern void _ZN12_GLOBAL__N_18TriangleD1Ev(const struct Triangle *);
extern __attribute__((noreturn)) void __stack_chk_fail(void);
const Shape * shape;
long local_18;
unsigned char local_28[8];
unsigned char local_30[8];
unsigned char local_38[8];
long ret;
long var18;
int var24;
long var28;
long var34;
long var35;
long var37;
long var44;
long var49;
long var8;
// x86-64 prologue: save rbp, frame 8 bytes
local_18 = (long)(0x28);
*(long *)(&local_38[0]) = 0x3d48;
*(long *)(&local_30[0]) = 0x3d10;
*(long *)(&local_28[0]) = 0x3cd8;
var8 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(arg0)) & 3)));
if (((unsigned long)((unsigned int)(var8)) == 0)) {
shape = (const Shape *)&local_38[0];
} else {
if (((unsigned long)((unsigned int)(var8)) == 1)) {
shape = (const Shape *)&local_30[0];
} else {
shape = (const Shape *)&local_28[0];
}
}
var18 = ((long (*)(char *, long, long))(*(long *)((*(long *)((long)shape) + 24))))((char *)((long)shape), (unsigned long)((unsigned int)(arg1)), (unsigned long)((unsigned int)(arg2)));
var24 = ((unsigned int)(((unsigned long)((unsigned int)(var18)) << 2)) + (unsigned int)(var18));
var28 = (unsigned long)((unsigned int)(((unsigned long)((unsigned int)(var24)) + (unsigned long)((unsigned int)(var24)))));
var34 = ((long (*)(char *))(*(long *)((*(long *)((long)shape) + 32))))((char *)((long)shape));
var35 = var34;
var37 = (unsigned long)((unsigned int)((var28 + var34)));
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleD1Ev)((char *)(&local_28[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD1Ev)((char *)(&local_30[0]));
((void (*)(char *))_ZN12_GLOBAL__N_14RectD2Ev)((char *)(&local_38[0]));
ret = (unsigned long)((unsigned int)(var37));
if ((local_18 == 0x28)) {
// x86-64 epilogue: restore rbp
return ret;
}
__stack_chk_fail();
// __glaurung_eh_landing_14cf
var44 = var35;
((void (*)(char *))_ZN12_GLOBAL__N_18TriangleD1Ev)((char *)(&local_28[0]));
((void (*)(char *))_ZN12_GLOBAL__N_16SquareD1Ev)((char *)(&local_30[0]));
((void (*)(char *))_ZN12_GLOBAL__N_14RectD2Ev)((char *)(&local_38[0]));
var49 = _Unwind_Resume(var44);
} gcc -O2
1/3cpp_vtable_area fail 35 lines
// glaurung: cpp_vtable_area @ 0x1230
typedef struct Shape Shape;
int32_t cpp_vtable_area(int32_t arg0, int32_t arg1, int32_t arg2) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
const Shape * shape;
long local_10;
unsigned char local_18[8];
unsigned char local_20[8];
unsigned char local_28[8];
long ret;
long var1;
long var11;
long var7;
var1 = (unsigned long)((unsigned int)((arg0 & 3)));
local_10 = (long)(0x28);
*(long *)(&local_28[0]) = 0x3d20;
*(long *)(&local_20[0]) = 0x3d58;
*(long *)(&local_18[0]) = 0x3d90;
var7 = (long)(0x1170);
shape = (const Shape *)&local_20[0];
if (((unsigned long)((unsigned int)(var1)) != 1)) {
var7 = (long)(0x1150);
shape = (const Shape *)&local_28[0];
if (((unsigned long)((unsigned int)(var1)) == 2)) {
var7 = (long)(0x1190);
shape = (const Shape *)&local_18[0];
}
}
var11 = ((long (*)(char *))(var7))((char *)((long)shape));
ret = var11;
if ((local_10 != 0x28)) {
__stack_chk_fail();
}
return ret;
} cpp_vtable_devirtualized pass 4 lines
// glaurung: cpp_vtable_devirtualized @ 0x1360
int32_t cpp_vtable_devirtualized(int32_t arg0, int32_t arg1) {
return (unsigned int)(((unsigned long)((unsigned int)((arg0 * arg0))) + ((unsigned long)((unsigned int)((arg1 + arg0))) * 2)));
} cpp_vtable_inherited_slot fail 46 lines
// glaurung: cpp_vtable_inherited_slot @ 0x12c0
typedef struct Shape Shape;
int32_t cpp_vtable_inherited_slot(int32_t arg0, int32_t arg1, int32_t arg2) {
extern __attribute__((noreturn)) void __stack_chk_fail(void);
const Shape * shape;
long rbp;
long ret;
long rsp;
unsigned char stack_3[16];
unsigned char stack_5[16];
long stack_top;
long var11;
long var4;
int var5;
long var6;
long var7;
long var9;
rsp = (rsp - 8);
rsp = (rsp - 8);
rsp = (rsp - 40);
*(long *)((&stack_5[0] + 8)) = (long)((long)(0x28));
rbp = rsp;
*(long *)(&stack_3[0]) = 0x3d20;
*(long *)((&stack_3[0] + 8)) = 0x3d58;
*(long *)(&stack_5[0]) = 0x3d90;
var4 = (long)(0x1140);
var5 = (arg0 & 3);
var6 = (unsigned long)((unsigned int)(var5));
if (((unsigned long)((unsigned int)(var5)) != 0)) {
var4 = (long)(0x11b0);
rbp = (long)(&stack_5[0]);
if (((unsigned long)((unsigned int)(var6)) == 1)) {
var4 = (long)(0x1140);
rbp = (rsp + 8);
}
}
var7 = ((long (*)(long *))(var4))((long *)(rbp));
var9 = (unsigned long)((unsigned int)(var7));
var11 = ((long (*)(long *))(*(long *)((stack_top + 0x20))))((long *)(rbp));
ret = (unsigned long)((unsigned int)((var11 + ((unsigned long)((unsigned int)((var9 + (var9 * 4)))) * 2))));
if ((*(long *)((&stack_5[0] + 8)) != 0x28)) {
__stack_chk_fail();
}
// x86-64 epilogue: restore rbp
return ret;
}