From 103d8034871ac4e28c4d1151be52aac892ffc20d Mon Sep 17 00:00:00 2001 From: Andreas Fertig Date: Sat, 29 Jun 2024 13:44:39 +0200 Subject: [PATCH] Added support for vtable visualization in Cfront mode. Unsupported for now is virtual inheritance as well as how a `dynamic_cast` translates. --- ASTHelpers.cpp | 50 +++ ASTHelpers.h | 5 + CfrontCodeGenerator.cpp | 409 ++++++++++++++++++++--- CodeGenerator.cpp | 48 ++- CodeGenerator.h | 32 ++ CoroutinesCodeGenerator.cpp | 13 +- Insights.cpp | 13 + Insights.h | 3 + InsightsHelpers.cpp | 3 + docs/cmdl-examples/edu-show-cfront.cpp | 62 +++- tests/EduCfrontTest5.expect | 30 +- tests/EduCfrontTest7.expect | 31 +- tests/EduCfrontTest8.expect | 2 +- tests/EduCfrontVtable2Test.cpp | 47 +++ tests/EduCfrontVtable2Test.expect | 209 ++++++++++++ tests/EduCfrontVtable3Test.cpp | 33 ++ tests/EduCfrontVtable3Test.expect | 167 +++++++++ tests/EduCfrontVtable4Test.cpp | 35 ++ tests/EduCfrontVtable4Test.expect | 176 ++++++++++ tests/EduCfrontVtable5Test.cpp | 30 ++ tests/EduCfrontVtable5Test.expect | 132 ++++++++ tests/EduCfrontVtable6Test.cpp | 29 ++ tests/EduCfrontVtable6Test.expect | 145 ++++++++ tests/EduCfrontVtablePureFuncTest.cpp | 28 ++ tests/EduCfrontVtablePureFuncTest.expect | 124 +++++++ tests/EduCfrontVtableTest.cpp | 26 ++ tests/EduCfrontVtableTest.expect | 124 +++++++ 27 files changed, 1941 insertions(+), 65 deletions(-) create mode 100644 tests/EduCfrontVtable2Test.cpp create mode 100644 tests/EduCfrontVtable2Test.expect create mode 100644 tests/EduCfrontVtable3Test.cpp create mode 100644 tests/EduCfrontVtable3Test.expect create mode 100644 tests/EduCfrontVtable4Test.cpp create mode 100644 tests/EduCfrontVtable4Test.expect create mode 100644 tests/EduCfrontVtable5Test.cpp create mode 100644 tests/EduCfrontVtable5Test.expect create mode 100644 tests/EduCfrontVtable6Test.cpp create mode 100644 tests/EduCfrontVtable6Test.expect create mode 100644 tests/EduCfrontVtablePureFuncTest.cpp create mode 100644 tests/EduCfrontVtablePureFuncTest.expect create mode 100644 tests/EduCfrontVtableTest.cpp create mode 100644 tests/EduCfrontVtableTest.expect diff --git a/ASTHelpers.cpp b/ASTHelpers.cpp index 831b10a7..e381f9b1 100644 --- a/ASTHelpers.cpp +++ b/ASTHelpers.cpp @@ -193,6 +193,22 @@ CanQualType VoidTy() } //----------------------------------------------------------------------------- +ParenExpr* Paren(Expr* expr) +{ + return new(GetGlobalAST()) ParenExpr({}, {}, expr); +} +//----------------------------------------------------------------------------- + +QualType ContantArrayTy(QualType t, int size) +{ + return GetGlobalAST().getConstantArrayType(t, + llvm::APInt(32, size, false), + /*const Expr* SizeExpr*/ nullptr, + ArraySizeModifier::Normal, + 0u); +} +//----------------------------------------------------------------------------- + static QualType mkAnonVoidFunctionPointer() { auto voidPtr = Ptr(VoidTy()); @@ -215,6 +231,25 @@ CXXStaticCastExpr* CastToVoidFunPtr(std::string_view name) } //----------------------------------------------------------------------------- +CXXReinterpretCastExpr* ReinterpretCast(QualType toType, const Expr* toExpr, bool makePointer) +{ + auto& ctx = GetGlobalAST(); + + QualType sourceInfoToType = makePointer ? Ptr(toType) : toType; + + return CXXReinterpretCastExpr::Create(ctx, + toType, + VK_LValue, + CK_BitCast, + const_cast(toExpr), + nullptr, + ctx.getTrivialTypeSourceInfo(sourceInfoToType), + {}, + {}, + {}); +} +//----------------------------------------------------------------------------- + CXXStaticCastExpr* StaticCast(QualType toType, const Expr* toExpr, bool makePointer) { auto& ctx = GetGlobalAST(); @@ -298,6 +333,12 @@ BinaryOperator* Equal(Expr* var, Expr* assignExpr) } //----------------------------------------------------------------------------- +BinaryOperator* Plus(Expr* var, Expr* assignExpr) +{ + return mkBinaryOperator(var, assignExpr, BO_Add, var->getType()); +} +//----------------------------------------------------------------------------- + GotoStmt* Goto(std::string_view labelName) { return new(GetGlobalAST()) GotoStmt(mkLabelDecl(labelName), {}, {}); @@ -511,6 +552,15 @@ FieldDecl* mkFieldDecl(DeclContext* dc, std::string_view name, QualType type) } //----------------------------------------------------------------------------- +InitListExpr* InitList(ArrayRef initExprs, QualType t) +{ + auto* initList = new(GetGlobalAST()) InitListExpr(GetGlobalAST(), SourceLocation{}, initExprs, SourceLocation{}); + initList->setType(t); + + return initList; +} +//----------------------------------------------------------------------------- + ArraySubscriptExpr* ArraySubscript(const Expr* lhs, uint64_t index, QualType type) { return new(GetGlobalAST()) ArraySubscriptExpr( diff --git a/ASTHelpers.h b/ASTHelpers.h index ba5c0bce..ca36d7b3 100644 --- a/ASTHelpers.h +++ b/ASTHelpers.h @@ -107,6 +107,9 @@ DeclRefExpr* mkDeclRefExpr(const ValueDecl* vd); NullStmt* mkNullStmt(); FieldDecl* mkFieldDecl(DeclContext* dc, std::string_view name, QualType type); +ParenExpr* Paren(Expr*); +QualType ContantArrayTy(QualType t, int size); +InitListExpr* InitList(ArrayRef initExprs, QualType t); ArraySubscriptExpr* ArraySubscript(const Expr* lhs, uint64_t index, QualType type); MemberExpr* AccessMember(const Expr* expr, const ValueDecl* vd, bool isArrow = true); CXXMemberCallExpr* CallMemberFun(Expr* memExpr, QualType retType); @@ -120,6 +123,8 @@ BinaryOperator* Assign(const VarDecl* var, Expr* assignExpr); BinaryOperator* Assign(UnaryOperator* var, Expr* assignExpr); BinaryOperator* Assign(Expr* var, Expr* assignExpr); BinaryOperator* Equal(Expr* var, Expr* assignExpr); +BinaryOperator* Plus(Expr* var, Expr* assignExpr); +CXXReinterpretCastExpr* ReinterpretCast(QualType toType, const Expr* toExpr, bool makePointer = false); CXXStaticCastExpr* StaticCast(QualType toType, const Expr* toExpr, bool makePointer = false); CXXStaticCastExpr* CastToVoidFunPtr(std::string_view name); CXXStaticCastExpr* Cast(const Expr* toExpr, QualType toType); diff --git a/CfrontCodeGenerator.cpp b/CfrontCodeGenerator.cpp index 4eca7d4f..79d9ede3 100644 --- a/CfrontCodeGenerator.cpp +++ b/CfrontCodeGenerator.cpp @@ -5,8 +5,11 @@ * ****************************************************************************/ +#include + #include #include + #include "CodeGenerator.h" #include "DPrint.h" #include "Insights.h" @@ -23,6 +26,10 @@ namespace clang::insights { using namespace asthelpers; //----------------------------------------------------------------------------- +//! Store the `this` pointer offset from derived to base class. +static llvm::DenseMap, int> mThisPointerOffset{}; +//----------------------------------------------------------------------------- + static MemberExpr* AccessMember(std::string_view name, const ValueDecl* vd, QualType type) { auto* rhsDeclRef = mkVarDeclRefExpr(name, type); @@ -32,6 +39,45 @@ static MemberExpr* AccessMember(std::string_view name, const ValueDecl* vd, Qual } //----------------------------------------------------------------------------- +CfrontCodeGenerator::CfrontVtableData::CfrontVtableData() +: vptpTypedef{Typedef("__vptp"sv, Function("vptp"sv, GetGlobalAST().IntTy, {})->getType())} +, vtableRecorDecl{} +{ + vtableRecorDecl = Struct("__mptr"sv); + auto AddField = [&](FieldDecl* field) { vtableRecorDecl->addDecl(field); }; + + d = mkFieldDecl(vtableRecorDecl, "d"sv, GetGlobalAST().ShortTy); + AddField(d); + AddField(mkFieldDecl(vtableRecorDecl, "i"sv, GetGlobalAST().ShortTy)); + f = mkFieldDecl(vtableRecorDecl, "f"sv, vptpTypedef); + AddField(f); + + vtableRecorDecl->completeDefinition(); + + vtableRecordType = QualType{vtableRecorDecl->getTypeForDecl(), 0u}; +} +//----------------------------------------------------------------------------- + +VarDecl* CfrontCodeGenerator::CfrontVtableData::VtblArrayVar(int size) +{ + return Variable("__vtbl_array"sv, ContantArrayTy(Ptr(vtableRecordType), size)); +} +//----------------------------------------------------------------------------- + +FieldDecl* CfrontCodeGenerator::CfrontVtableData::VtblPtrField(const CXXRecordDecl* parent) +{ + return mkFieldDecl(const_cast(parent), StrCat("__vptr"sv, GetName(*parent)), Ptr(vtableRecordType)); +} +//----------------------------------------------------------------------------- + +CfrontCodeGenerator::CfrontVtableData& CfrontCodeGenerator::VtableData() +{ + static CfrontVtableData data{}; + + return data; +} +//----------------------------------------------------------------------------- + CodeGeneratorVariant::CodeGenerators::CodeGenerators(OutputFormatHelper& _outputFormatHelper, CodeGenerator::LambdaStackType& lambdaStack, CodeGenerator::ProcessingPrimaryTemplate processingPrimaryTemplate) @@ -406,10 +452,6 @@ void CfrontCodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBod auto* body = stmt->getBody(); StmtsContainer bodyStmts{}; - if(body) { - bodyStmts.AddBodyStmts(body); - } - auto retType = stmt->getReturnType(); auto processBaseClassesAndFields = [&](const CXXRecordDecl* parent) { @@ -514,11 +556,37 @@ void CfrontCodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBod for(const auto& base : parent->bases()) { auto baseType = base.getType(); - bodyStmts.AddBodyStmts(CallConstructor(baseType, Ptr(baseType), nullptr, {}, DoCast::Yes)); + + if(const auto* baseRd = baseType->getAsCXXRecordDecl(); + baseRd and baseRd->hasNonTrivialDefaultConstructor()) { + bodyStmts.AddBodyStmts(CallConstructor(baseType, Ptr(baseType), nullptr, {}, DoCast::Yes)); + } insertFields(baseType->getAsRecordDecl()); } + auto insertVtblPtr = [&](const CXXRecordDecl* cur) { + if(cur->isPolymorphic() and (0 == cur->getNumBases())) { + auto* fieldDecl = VtableData().VtblPtrField(cur); + auto* lhsMemberExpr = AccessMember(kwInternalThis, fieldDecl, Ptr(GetRecordDeclType(cur))); + + // struct __mptr *__ptbl_vec__c___src_C_[] + auto* vtablAr = VtableData().VtblArrayVar(1); + auto* vtblArrayPos = ArraySubscript( + mkDeclRefExpr(vtablAr), GetGlobalVtablePos(stmt->getParent(), cur), fieldDecl->getType()); + + bodyStmts.AddBodyStmts(Assign(lhsMemberExpr, fieldDecl, vtblArrayPos)); + } + }; + + // insert our vtable pointer + insertVtblPtr(stmt->getParent()); + + // in case of multi inheritance insert additional vtable pointers + for(const auto& base : parent->bases()) { + insertVtblPtr(base.getType()->getAsCXXRecordDecl()); + } + // insert own fields insertFields(parent); @@ -554,9 +622,11 @@ void CfrontCodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBod } } - auto* lhsDeclRef = mkVarDeclRefExpr(kwInternalThis, Ptr(ctorDecl->getType())); + if(body) { + bodyStmts.AddBodyStmts(body); + } - bodyStmts.AddBodyStmts(Return(lhsDeclRef)); + bodyStmts.AddBodyStmts(Return(mkVarDeclRefExpr(kwInternalThis, Ptr(ctorDecl->getType())))); body = mkCompoundStmt({bodyStmts}); retType = parentType; @@ -569,11 +639,11 @@ void CfrontCodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBod bodyStmts.clear(); processBaseClassesAndFields(stmt->getParent()); + } else if(body) { + bodyStmts.AddBodyStmts(body); } - auto* lhsDeclRef = mkVarDeclRefExpr(kwInternalThis, Ptr(stmt->getType())); - - bodyStmts.AddBodyStmts(Return(lhsDeclRef)); + bodyStmts.AddBodyStmts(Return(mkVarDeclRefExpr(kwInternalThis, Ptr(stmt->getType())))); body = mkCompoundStmt({bodyStmts}); retType = parentType; @@ -581,6 +651,10 @@ void CfrontCodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBod } else if(const auto* dtor = dyn_cast_or_null(stmt)) { // Based on: https://www.dre.vanderbilt.edu/~schmidt/PDF/C++-translation.pdf + if(body) { + bodyStmts.AddBodyStmts(body); + } + if(not HasDtor(GetRecordDeclType(dtor->getParent()))) { return; } @@ -635,11 +709,46 @@ void CfrontCodeGenerator::InsertCXXMethodDecl(const CXXMethodDecl* stmt, SkipBod void CfrontCodeGenerator::FormatCast(const std::string_view, const QualType& castDestType, const Expr* subExpr, - const CastKind&) + const CastKind& kind) { // C does not have a rvalue notation and we already transformed the temporary into an object. Skip the cast to &&. - if(not castDestType->isRValueReferenceType()) { + // Ignore CK_UncheckedDerivedToBase which would lead to (A)c where neither A nor c is a pointer. + if(not castDestType->isRValueReferenceType() and not(CastKind::CK_UncheckedDerivedToBase == kind)) { mOutputFormatHelper.Append("(", GetName(castDestType), ")"); + + // ARM p 221: + // C* pc = new C; + // B* pb = pc -> pc = (B*) ((char*)pc+delta(B)) + if(is{kind}.any_of(CastKind::CK_DerivedToBase, CastKind::CK_BaseToDerived)) { + // We have to switch in case of a base to derived cast + auto [key, + sign] = [&]() -> std::pair, std::string_view> { + auto plainType = [](QualType t) { + if(const auto* pt = dyn_cast_or_null(t.getTypePtrOrNull())) { + return pt->getPointeeType()->getAsCXXRecordDecl(); + } + + return t->getAsCXXRecordDecl(); + }; + + auto base = plainType(castDestType); + auto derived = plainType(subExpr->getType()); + + if((CastKind::CK_BaseToDerived == kind)) { + return {{base, derived}, "-"sv}; + } + + return {{derived, base}, "+"sv}; + }(); + + if(auto off = mThisPointerOffset[key]) { + mOutputFormatHelper.Append("((char*)"sv); + InsertArg(subExpr); + mOutputFormatHelper.Append(sign, off, ")"sv); + + return; + } + } } InsertArg(subExpr); @@ -680,52 +789,196 @@ void CfrontCodeGenerator::InsertArg(const TypedefDecl* stmt) } //----------------------------------------------------------------------------- +static void ProcessFields(CXXRecordDecl* recordDecl, const CXXRecordDecl* rd) +{ + RETURN_IF(not rd->hasDefinition()) + + auto AddField = [&](const FieldDecl* field) { + recordDecl->addDecl(mkFieldDecl(recordDecl, GetName(*field), field->getType())); + }; + + // Insert field from base classes + for(const auto& base : rd->bases()) { + // XXX: ignoring TemplateSpecializationType + if(const auto* rdBase = dyn_cast_or_null(base.getType().getCanonicalType()->getAsRecordDecl())) { + ProcessFields(recordDecl, rdBase); + } + } + + // insert vtable pointer if required + if(rd->isPolymorphic() and (rd->getNumBases() == 0)) { + recordDecl->addDecl(CfrontCodeGenerator::VtableData().VtblPtrField(rd)); + } + + // insert own fields + for(const auto* d : rd->fields()) { + AddField(d); + } + + if(recordDecl->field_empty()) { + AddField(mkFieldDecl(recordDecl, "__dummy"sv, GetGlobalAST().CharTy)); + } +} +//----------------------------------------------------------------------------- + +static std::string GetFirstPolymorphicBaseName(const RecordDecl* decl, const RecordDecl* to) +{ + std::string ret{GetName(*decl)}; + + if(const auto* rdecl = dyn_cast_or_null(decl); rdecl->getNumBases() > 1) { + for(const auto& base : rdecl->bases()) { + if(const auto* rd = base.getType()->getAsRecordDecl(); rd == to) { + ret += GetFirstPolymorphicBaseName(rd, to); + break; + } + } + } + + return ret; +} +//----------------------------------------------------------------------------- + void CfrontCodeGenerator::InsertArg(const CXXRecordDecl* stmt) { auto* recordDecl = Struct(GetName(*stmt)); - if(stmt->hasDefinition()) { - auto AddField = [&](const FieldDecl* field) { - auto* fieldDecl = mkFieldDecl(recordDecl, GetName(*field), field->getType()); + if(stmt->hasDefinition() and stmt->isPolymorphic()) { + if(auto* itctx = + static_cast(const_cast(GetGlobalAST()).getVTableContext())) { +#if 0 + // Get mangled RTTI name + auto* mc = const_cast(GetGlobalAST()).createMangleContext(nullptr); + SmallString<256> rttiName{}; + llvm::raw_svector_ostream out(rttiName); + mc->mangleCXXRTTI(QualType(stmt->getTypeForDecl(), 0), out); + DPrint("name: %s\n", rttiName.c_str()); +#endif - recordDecl->addDecl(fieldDecl); - }; + SmallVector mInitExprs{}; + SmallVector baseList{}; - // Insert field from base classes - for(const auto& base : stmt->bases()) { - // XXX: ignoring TemplateSpecializationType - if(const auto* rd = base.getType().getCanonicalType()->getAsRecordDecl()) { - for(const auto* d : rd->fields()) { - AddField(d); - } + if(stmt->getNumBases() == 0) { + baseList.push_back(GetRecordDeclType(stmt)); } - } - // insert own fields - for(const auto* d : stmt->fields()) { - AddField(d); - } + for(const auto& base : stmt->bases()) { + baseList.push_back(base.getType()); + } + + llvm::DenseMap thunkMap{}; + const VTableLayout& layout{itctx->getVTableLayout(stmt)}; + + for(const auto& [idx, thunk] : layout.vtable_thunks()) { + thunkMap[idx] = thunk; + } + + unsigned clsIdx{}; + unsigned funIdx{}; + auto& vtblData = VtableData(); + + auto pushVtable = [&] { + if(funIdx) { + EnableGlobalInsert(GlobalInserts::FuncVtableStruct); + + // struct __mptr __vtbl__A[] = {0, 0, 0, 0, 0, (__vptp)FunA, 0, 0, 0}; + auto* thisRd = baseList[clsIdx - 1]->getAsCXXRecordDecl(); + auto vtableName{StrCat("__vtbl_"sv, GetFirstPolymorphicBaseName(stmt, thisRd))}; + auto* vtabl = Variable(vtableName, ContantArrayTy(vtblData.vtableRecordType, funIdx)); + vtabl->setInit(InitList(mInitExprs, vtblData.vtableRecordType)); + + PushVtableEntry(stmt, thisRd, vtabl); + + funIdx = 0; + } - if(recordDecl->field_empty()) { - AddField(mkFieldDecl(recordDecl, "__dummy"sv, GetGlobalAST().CharTy)); + mInitExprs.clear(); + }; + + for(unsigned i = 0; const auto& vc : layout.vtable_components()) { + switch(vc.getKind()) { + case VTableComponent::CK_OffsetToTop: { + auto off = layout.getVTableOffset(clsIdx); + if(auto rem = (off % 4)) { + off += 4 - rem; // sometimes the value is misaligned. Align to 4 bytes + } + + mThisPointerOffset[{stmt, baseList[clsIdx]->getAsCXXRecordDecl()}] = off * 4; // we need bytes + + if(clsIdx >= 1) { + pushVtable(); + } + ++clsIdx; + } break; + + case VTableComponent::CK_RTTI: + break; + + // Source: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-components + // The entries for virtual destructors are actually pairs of entries. The first destructor, + // called the complete object destructor, performs the destruction without calling delete() on + // the object. The second destructor, called the deleting destructor, calls delete() after + // destroying the object. + case VTableComponent::CK_CompleteDtorPointer: + break; // vc.getKind() == VTableComponent::CK_CompleteDtorPointer + case VTableComponent::CK_DeletingDtorPointer: + case VTableComponent::CK_FunctionPointer: { + auto* thunkOffset = [&] { + if(ThunkInfo thunk = thunkMap.lookup(i); not thunk.isEmpty() and not thunk.This.isEmpty()) { + return Int32(thunk.This.NonVirtual); + } + + return Int32(0); + }(); + + const auto* md = dyn_cast_or_null(vc.getFunctionDecl()); + + std::string name{}; + if(md->isPureVirtual()) { + EnableGlobalInsert(GlobalInserts::HeaderStdlib); + EnableGlobalInsert(GlobalInserts::FuncCxaPureVirtual); + + md = Function("__cxa_pure_virtual"sv, VoidTy(), params_vector{{kwInternalThis, VoidTy()}}); + + name = GetName(*md); + } else { + name = GetSpecialMemberName(md); + } + + auto* reicast = ReinterpretCast(vtblData.vptpTypedef, mkVarDeclRefExpr(name, md->getType())); + + mInitExprs.push_back(InitList({thunkOffset, Int32(0), reicast}, vtblData.vtableRecordType)); + + ++funIdx; + break; + } + default: break; + } + + ++i; + } + + pushVtable(); } + } + if(stmt->hasDefinition()) { + ProcessFields(recordDecl, stmt); recordDecl->completeDefinition(); + mOutputFormatHelper.Append(kwTypedefSpace); + } + + // use our freshly created recordDecl + CodeGenerator::InsertArg(recordDecl); + #if 0 // TypedefDecl above is not called auto& ctx = GetGlobalAST(); auto et = ctx.getElaboratedType(ElaboratedTypeKeyword::ETK_Struct, nullptr, GetRecordDeclType(recordDecl), nullptr); auto* typedefDecl = Typedef(GetName(*stmt),et); + CodeGenerator::InsertArg(typedefDecl); #endif - mOutputFormatHelper.Append(kwTypedefSpace); - } - - // use our freshly created recordDecl - CodeGenerator::InsertArg(recordDecl); - // CodeGenerator::InsertArg(typedefDecl); - // insert member functions except for the special member functions and classes defined inside this class for(OnceTrue firstRecordDecl{}; const auto* d : stmt->decls()) { if((isa(d) and firstRecordDecl) // skip the first record decl which are ourselves @@ -747,14 +1000,35 @@ void CfrontCodeGenerator::InsertArg(const CXXRecordDecl* stmt) } //----------------------------------------------------------------------------- +///! Find the first polymorphic base class. +static const CXXRecordDecl* GetFirstPolymorphicBase(const RecordDecl* decl) +{ + if(const auto* rdecl = dyn_cast_or_null(decl); rdecl->getNumBases() >= 1) { + for(const auto& base : rdecl->bases()) { + const auto* rd = base.getType()->getAsRecordDecl(); + + if(const auto* cxxRd = dyn_cast_or_null(rd); not cxxRd or not cxxRd->isPolymorphic()) { + continue; + } else if(const CXXRecordDecl* ret = GetFirstPolymorphicBase(rd)) { + return ret; + } + + break; + } + } + + return dyn_cast_or_null(decl); +} +//----------------------------------------------------------------------------- + void CfrontCodeGenerator::InsertArg(const CXXMemberCallExpr* stmt) { if(const auto* me = dyn_cast_or_null(stmt->getCallee())) { - auto* obj = me->getBase(); + auto* obj = me->getBase(); + const bool isPointer{obj->getType()->isPointerType()}; - const bool isReference = IsReferenceType(dyn_cast_or_null(obj->getReferencedDeclOfCallee())); - - if(not obj->getType()->isPointerType() and not isReference) { + if(const bool isReference = IsReferenceType(dyn_cast_or_null(obj->getReferencedDeclOfCallee())); + not isPointer and not isReference) { obj = Ref(obj); } @@ -766,8 +1040,10 @@ void CfrontCodeGenerator::InsertArg(const CXXMemberCallExpr* stmt) } } + auto* memDecl = me->getMemberDecl(); + if(const auto* ar = dyn_cast_or_null(obj->getType())) { - if(const auto* dtor = dyn_cast_or_null(me->getMemberDecl())) { + if(const auto* dtor = dyn_cast_or_null(memDecl)) { // ignore the reference InsertArg(CallVecDtor(dyn_cast_or_null(obj)->getSubExpr(), ar)); return; @@ -778,7 +1054,52 @@ void CfrontCodeGenerator::InsertArg(const CXXMemberCallExpr* stmt) auto* ncStmt = const_cast(stmt); params.append(ncStmt->arg_begin(), ncStmt->arg_end()); - InsertArg(Call(GetSpecialMemberName(me->getMemberDecl()), params)); + if(auto* md = dyn_cast_or_null(memDecl); md and md->isVirtual()) { + auto& vtblData = VtableData(); + auto* cls = md->getParent(); + auto vRecordDecl = GetFirstPolymorphicBase(cls); + auto* vtblField = VtableData().VtblPtrField(vRecordDecl); + + // -- cast to function signature: void Fun(struct X*) + + auto destType = not isPointer ? Ptr(obj->getType()) : obj->getType(); + auto atype = isPointer ? obj->getType()->getPointeeType() : obj->getType(); + auto idx = mVirtualFunctions[{md, {atype->getAsCXXRecordDecl(), vRecordDecl}}]; + + // a->__vptr[1]; #1 + auto* accessVptr = AccessMember(Paren(obj), vtblField, true); + auto* vtblArrayPos = ArraySubscript(accessVptr, idx, vtblField->getType()); + + auto* p = Paren(vtblArrayPos); // ( #1 ) #2 + auto* accessMemberF = AccessMember(p, vtblData.f, false); // #2.f #3 + + // (void (*)(struct X*) (#3) + params_vector ps{{kwInternalThis, destType}}; + auto* funcPtrFuncDecl = Function("__dummy"sv, VoidTy(), ps); + auto* reicast = ReinterpretCast(funcPtrFuncDecl->getType(), accessMemberF, true); + + auto* p4 = Paren(reicast); // (#4) + auto p5 = Dref(p4); // *#5 + auto* p6 = Paren(p5); // (#5) #6 + + // -- call with possible this pointer adjustment + + auto* p7 = AccessMember(p, vtblData.d, false); // a->__vptr[1]; #7 + auto* p8 = ReinterpretCast(GetGlobalAST().CharTy, Paren(obj), true); // (#7) #8 + + auto* p9 = ReinterpretCast(destType, p8); + + auto* p10 = Paren(p9); + auto* p11 = Plus(p10, p7); // #7 + #8 #9 + auto* p12 = Paren(p11); + + // Use the modified object parameter + params[0] = p12; + InsertArg(CallExpr::Create(GetGlobalAST(), p6, params, p6->getType(), VK_LValue, {}, {})); + + } else { + InsertArg(Call(GetSpecialMemberName(memDecl), params)); + } } else { CodeGenerator::InsertArg(stmt); diff --git a/CodeGenerator.cpp b/CodeGenerator.cpp index 29cf049e..973e238a 100644 --- a/CodeGenerator.cpp +++ b/CodeGenerator.cpp @@ -1062,17 +1062,38 @@ class TemporaryDeclFinder : public StmtVisitor }; //----------------------------------------------------------------------------- +/*constinit*/ static SmallVector, VarDecl*>, 10> + gVtables{}; /*constinit*/ static SmallVector globalVarCtors{}; /*constinit*/ static SmallVector globalVarDtors{}; //----------------------------------------------------------------------------- -void PushGlobalVariable(const Expr* callExpr) +int GetGlobalVtablePos(const CXXRecordDecl* record, const CXXRecordDecl* recordB) +{ + auto iter = std::ranges::find_if( + gVtables, [&](const auto& e) { return (e.first.first == record) and (e.first.second == recordB); }); + + if(iter == gVtables.end()) { + iter = std::ranges::find_if(gVtables, [&](const auto& e) { return e.first.first == record; }); + } + + return std::distance(gVtables.begin(), iter); +} +//----------------------------------------------------------------------------- + +void PushVtableEntry(const CXXRecordDecl* record, const CXXRecordDecl* recordB, VarDecl* decl) +{ + gVtables.push_back({{record, recordB}, decl}); +} +//----------------------------------------------------------------------------- + +static void PushGlobalVariable(const Expr* callExpr) { globalVarCtors.push_back(const_cast(callExpr)); } //----------------------------------------------------------------------------- -void PushGlobalVariableDtor(const Expr* callExpr) +static void PushGlobalVariableDtor(const Expr* callExpr) { globalVarDtors.push_back(const_cast(callExpr)); } @@ -1093,6 +1114,26 @@ std::string EmitGlobalVariableCtors() ofm.AppendNewLine(); ofm.AppendNewLine(); CodeGeneratorVariant cg{ofm}; + + if(gVtables.size()) { + SmallVector mInitExprs{}; + + for(auto& e : gVtables) { + cg->InsertArg(e.second); + mInitExprs.push_back(mkDeclRefExpr(e.second)); + } + + ofm.AppendNewLine(); + + // struct __mptr *__ptbl_vec__c___src_C_[] + auto* vtable = CfrontCodeGenerator::VtableData().VtblArrayVar(mInitExprs.size()); + vtable->setInit(InitList(mInitExprs, vtable->getType())); + + cg->InsertArg(vtable); + + ofm.AppendNewLine(); + } + cg->InsertArg(cxaStartFun); StmtsContainer bodyStmtsDtors{}; @@ -1299,7 +1340,8 @@ void CodeGenerator::InsertArg(const VarDecl* stmt) if(MyOptional initList{dyn_cast_or_null(init)}; GetInsightsOptions().UseShow2C and - initList.and_then(CanonicalType).and_then(Isa).and_not(IsPointer).and_then(IsPOD)) { + initList.and_then(CanonicalType).and_then(Isa).and_not(IsPointer).and_then(IsPOD) and + not isa(stmt->getType())) { auto* callMemset = Call("memset"sv, {Ref(stmt), Int32(0), Sizeof(stmt->getType())}); EnableGlobalInsert(GlobalInserts::FuncMemset); diff --git a/CodeGenerator.h b/CodeGenerator.h index 093555b1..2d6dea9f 100644 --- a/CodeGenerator.h +++ b/CodeGenerator.h @@ -24,6 +24,9 @@ namespace clang::insights { +void PushVtableEntry(const CXXRecordDecl*, const CXXRecordDecl*, VarDecl* decl); +int GetGlobalVtablePos(const CXXRecordDecl*, const CXXRecordDecl*); + class CppInsightsCommentStmt : public Stmt { std::string mComment{}; @@ -602,6 +605,9 @@ class CoroutinesCodeGenerator final : public CodeGenerator /// command line option. class CfrontCodeGenerator final : public CodeGenerator { + ///! A mapping for the pair method decl - derived-to-base-class to index in the vtable. + static inline llvm::DenseMap>, int> + mVirtualFunctions{}; bool mInsertSemi{true}; // We need to for int* p = new{5}; public: @@ -625,6 +631,32 @@ class CfrontCodeGenerator final : public CodeGenerator void FormatCast(const std::string_view, const QualType&, const Expr*, const CastKind&) override; + struct CfrontVtableData + { + CfrontVtableData(); + + // struct __mptr *__ptbl_vec__c___src_C_[] + VarDecl* VtblArrayVar(int size); + FieldDecl* VtblPtrField(const CXXRecordDecl* parent); + + QualType vptpTypedef; // typedef int (*__vptp)(); + + /* +struct __mptr +{ + short d; + short i; + __vptp f; +}; +*/ + CXXRecordDecl* vtableRecorDecl; + QualType vtableRecordType; + FieldDecl* d; + FieldDecl* f; + }; + + static CfrontVtableData& VtableData(); + protected: bool InsertSemi() override { return std::exchange(mInsertSemi, true); } }; diff --git a/CoroutinesCodeGenerator.cpp b/CoroutinesCodeGenerator.cpp index f6729404..e48685d7 100644 --- a/CoroutinesCodeGenerator.cpp +++ b/CoroutinesCodeGenerator.cpp @@ -476,18 +476,7 @@ void CoroutinesCodeGenerator::InsertCoroutine(const FunctionDecl& fd, const Coro mOutputFormatHelper.AppendCommentNewLine("Note: The actual parameter new is __builtin_coro_size"sv); auto* coroFrameVar = Variable(CORO_FRAME_NAME, GetFramePointerType()); - - CXXReinterpretCastExpr* reicast = - CXXReinterpretCastExpr::Create(ctx, - GetFramePointerType(), - VK_LValue, - CK_BitCast, - stmt->getAllocate(), - nullptr, - ctx.getTrivialTypeSourceInfo(GetFramePointerType()), - {}, - {}, - {}); + auto* reicast = ReinterpretCast(GetFramePointerType(), stmt->getAllocate()); coroFrameVar->setInit(reicast); diff --git a/Insights.cpp b/Insights.cpp index 4f1b07f4..11dce301 100644 --- a/Insights.cpp +++ b/Insights.cpp @@ -350,6 +350,7 @@ int main(int argc, const char** argv) AddGLobalInsertMapEntry(HeaderUtility, "#include // std::move"sv); AddGLobalInsertMapEntry(HeaderStddef, "#include // NULL and more"sv); AddGLobalInsertMapEntry(HeaderAssert, "#include // _Static_assert"sv); + AddGLobalInsertMapEntry(HeaderStdlib, "#include // abort"sv); // Now all the forward declared functions AddGLobalInsertMapEntry(FuncCxaStart, "void __cxa_start(void);"sv); @@ -370,6 +371,18 @@ int main(int argc, const char** argv) AddGLobalInsertMapEntry( FuncCxaVecDtor, R"(extern "C" void __cxa_vec_dtor(void *, unsigned int, unsigned int, void* (*destructor)(void *) );)"sv); + AddGLobalInsertMapEntry(FuncVtableStruct, R"(typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; +)"sv); + AddGLobalInsertMapEntry(FuncCxaPureVirtual, R"(extern "C" void __cxa_pure_virtual() { abort(); })"); llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); llvm::cl::SetVersionPrinter(&PrintVersion); diff --git a/Insights.h b/Insights.h index 9d9a3fb3..ff1f6ea2 100644 --- a/Insights.h +++ b/Insights.h @@ -44,6 +44,7 @@ enum class GlobalInserts HeaderUtility, //!< Track whether there was a std::move inserted. HeaderStddef, //!< Track whether we need to insert in Cfront mode HeaderAssert, //!< Track whether we need to insert in Cfront mode + HeaderStdlib, //!< Track whether we need to insert in Cfront mode // Now all the forward declared functions FuncCxaStart, @@ -56,6 +57,8 @@ enum class GlobalInserts FuncCxaVecCtor, FuncCxaVecDel, FuncCxaVecDtor, + FuncVtableStruct, + FuncCxaPureVirtual, // The traditional enum element count MAX diff --git a/InsightsHelpers.cpp b/InsightsHelpers.cpp index 54b697d3..85bfec6a 100644 --- a/InsightsHelpers.cpp +++ b/InsightsHelpers.cpp @@ -984,6 +984,9 @@ std::string GetCfrontOverloadedFunctionName(const FunctionDecl* fd) } } } + } else if(const auto* md = dyn_cast_or_null(fd); + md and GetInsightsOptions().UseShow2C and md->isVirtual()) { + name = GetName(*md->getParent()); } return name; diff --git a/docs/cmdl-examples/edu-show-cfront.cpp b/docs/cmdl-examples/edu-show-cfront.cpp index c4bd4145..43346e8a 100644 --- a/docs/cmdl-examples/edu-show-cfront.cpp +++ b/docs/cmdl-examples/edu-show-cfront.cpp @@ -1,7 +1,38 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +class A +{ +public: + int a; + virtual void v() { puts("A->v"); } +}; + +class B +{ +public: + int b; + virtual void w() { puts("B->w"); } +}; + +class C : public A, public B +{ +public: + int c; + void w() { puts("C->w"); } +}; + +class D : public A, public B +{ +public: + int d; +}; + class Apple { public: - Apple(){}; + Apple() {} Apple(int x) : mX{x} @@ -19,7 +50,7 @@ class Apple int mX{}; }; -int main() +void MemberFunctions() { Apple a{}; @@ -33,3 +64,30 @@ int main() b = a; } + +void Inheritance() +{ + C c; + + c.w(); + c.v(); + + B* b = &c; + b->w(); + + C* cb = (C*)(b); + cb->v(); + + // + D d; + B* bd = &d; + D* db = (D*)bd; + db->w(); +} + +int main() +{ + MemberFunctions(); + + Inheritance(); +} diff --git a/tests/EduCfrontTest5.expect b/tests/EduCfrontTest5.expect index ec0049fc..d8343faa 100644 --- a/tests/EduCfrontTest5.expect +++ b/tests/EduCfrontTest5.expect @@ -4,6 +4,17 @@ *************************************************************************************/ void __cxa_start(void); void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + typedef struct WithDefaultCtor { @@ -48,6 +59,7 @@ inline Type * operatorEqual(Type * __this, const Type * __rhs) typedef struct Base { + __mptr * __vptrBase; Type mY; } Base; @@ -63,6 +75,7 @@ inline Base * operatorEqual(Base * __this, const Base * __rhs) inline Base * Constructor_Base(Base * __this) { + __this->__vptrBase = __vtbl_array[0]; Constructor_Type(&__this->mY, 5); return __this; } @@ -70,6 +83,7 @@ inline Base * Constructor_Base(Base * __this) typedef struct BaseSecond { + __mptr * __vptrBaseSecond; Type mX; } BaseSecond; @@ -85,6 +99,7 @@ inline BaseSecond * operatorEqual(BaseSecond * __this, const BaseSecond * __rhs) inline BaseSecond * Constructor_BaseSecond(BaseSecond * __this) { + __this->__vptrBaseSecond = __vtbl_array[1]; Constructor_Type(&__this->mX, 5); return __this; } @@ -92,7 +107,9 @@ inline BaseSecond * Constructor_BaseSecond(BaseSecond * __this) typedef struct Derived { + __mptr * __vptrBase; Type mY; + __mptr * __vptrBaseSecond; Type mX; double mD; WithDefaultCtor mWd; @@ -110,7 +127,7 @@ inline Derived * operatorEqual(Derived * __this, const Derived * __rhs) operatorEqual(&__this->mY, &__rhs->mY); operatorEqual((Base *)__this, (Base *)__rhs); operatorEqual(&__this->mX, &__rhs->mX); - operatorEqual((BaseSecond *)__this, (BaseSecond *)__rhs); + operatorEqual((BaseSecond *)((char*)__this+16), (BaseSecond *)((char*)__rhs+16)); __this->mD = __rhs->mD; operatorEqual(&__this->mWd, &__rhs->mWd); return __this; @@ -122,6 +139,8 @@ inline Derived * Constructor_Derived(Derived * __this) Constructor_Type(&__this->mY, 5); Constructor_BaseSecond((BaseSecond *)__this); Constructor_Type(&__this->mX, 5); + __this->__vptrBase = __vtbl_array[2]; + __this->__vptrBaseSecond = __vtbl_array[3]; Constructor_WithDefaultCtor(&__this->mWd); return __this; } @@ -144,9 +163,16 @@ int main(int argc, const char ** argv) __cxa_atexit(); return ret; /* ret // lifetime ends here */ - Destructor_Derived(&d); + (*((void (*)(Derived *))((&d)->__vptrBase[0]).f))((((Derived *)(char *)(&d)) + ((&d)->__vptrBase[0]).d)); } +__mptr __vtbl_Base[1] = {0, 0, (__vptp)Destructor_Base}; +__mptr __vtbl_BaseSecond[1] = {0, 0, (__vptp)Destructor_BaseSecond}; +__mptr __vtbl_DerivedBase[1] = {0, 0, (__vptp)Destructor_Derived}; +__mptr __vtbl_DerivedBaseSecond[1] = {-16, 0, (__vptp)Destructor_Derived}; + +__mptr * __vtbl_array[4] = {__vtbl_Base, __vtbl_BaseSecond, __vtbl_DerivedBase, __vtbl_DerivedBaseSecond}; + void __cxa_start(void) { Constructor_Derived(&d); diff --git a/tests/EduCfrontTest7.expect b/tests/EduCfrontTest7.expect index 809446a0..7baf796d 100644 --- a/tests/EduCfrontTest7.expect +++ b/tests/EduCfrontTest7.expect @@ -4,6 +4,17 @@ *************************************************************************************/ void __cxa_start(void); void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + typedef struct Type { @@ -48,6 +59,7 @@ inline DefaultCtorType * operatorEqual(DefaultCtorType * __this, DefaultCtorType typedef struct Base { + __mptr * __vptrBase; Type mX; } Base; @@ -63,6 +75,7 @@ inline Base * operatorEqual(Base * __this, const Base * __rhs) inline Base * Constructor_Base(Base * __this) { + __this->__vptrBase = __vtbl_array[0]; Constructor_Type(&__this->mX, 5); return __this; } @@ -70,6 +83,7 @@ inline Base * Constructor_Base(Base * __this) typedef struct BaseSecond { + __mptr * __vptrBase; Type mX; Type mY; } BaseSecond; @@ -91,6 +105,7 @@ inline BaseSecond * Constructor_BaseSecond(BaseSecond * __this) { Constructor_Base((Base *)__this); Constructor_Type(&__this->mX, 5); + __this->__vptrBase = __vtbl_array[1]; Constructor_Type(&__this->mY, 5); return __this; } @@ -98,6 +113,7 @@ inline BaseSecond * Constructor_BaseSecond(BaseSecond * __this) typedef struct BaseThird { + __mptr * __vptrBaseThird; Type mZ; } BaseThird; @@ -113,6 +129,7 @@ inline BaseThird * operatorEqual(BaseThird * __this, const BaseThird * __rhs) inline BaseThird * Constructor_BaseThird(BaseThird * __this) { + __this->__vptrBaseThird = __vtbl_array[2]; Constructor_Type(&__this->mZ, 5); return __this; } @@ -120,7 +137,10 @@ inline BaseThird * Constructor_BaseThird(BaseThird * __this) typedef struct Derived { + __mptr * __vptrBase; + Type mX; Type mY; + __mptr * __vptrBaseThird; Type mZ; double mD; DefaultCtorType dt; @@ -139,7 +159,7 @@ inline Derived * operatorEqual(Derived * __this, const Derived * __rhs) operatorEqual(&__this->mY, &__rhs->mY); operatorEqual((BaseSecond *)__this, (BaseSecond *)__rhs); operatorEqual(&__this->mZ, &__rhs->mZ); - operatorEqual((BaseThird *)__this, (BaseThird *)__rhs); + operatorEqual((BaseThird *)((char*)__this+16), (BaseThird *)((char*)__rhs+16)); __this->mD = __rhs->mD; operatorEqual(&__this->dt, &__rhs->dt); __this->g = __rhs->g; @@ -152,6 +172,7 @@ inline Derived * Constructor_Derived(Derived * __this) Constructor_Type(&__this->mY, 5); Constructor_BaseThird((BaseThird *)__this); Constructor_Type(&__this->mZ, 5); + __this->__vptrBaseThird = __vtbl_array[4]; /* dt // trivial type, maybe uninitialized */ __this->g = 4; return __this; @@ -160,6 +181,14 @@ inline Derived * Constructor_Derived(Derived * __this) Derived d; +__mptr __vtbl_Base[1] = {0, 0, (__vptp)Destructor_Base}; +__mptr __vtbl_BaseSecond[1] = {0, 0, (__vptp)Destructor_BaseSecond}; +__mptr __vtbl_BaseThird[1] = {0, 0, (__vptp)Destructor_BaseThird}; +__mptr __vtbl_DerivedBaseSecond[1] = {0, 0, (__vptp)Destructor_Derived}; +__mptr __vtbl_DerivedBaseThird[1] = {-24, 0, (__vptp)Destructor_Derived}; + +__mptr * __vtbl_array[5] = {__vtbl_Base, __vtbl_BaseSecond, __vtbl_BaseThird, __vtbl_DerivedBaseSecond, __vtbl_DerivedBaseThird}; + void __cxa_start(void) { Constructor_Derived(&d); diff --git a/tests/EduCfrontTest8.expect b/tests/EduCfrontTest8.expect index 794c352e..9b5b972e 100644 --- a/tests/EduCfrontTest8.expect +++ b/tests/EduCfrontTest8.expect @@ -17,9 +17,9 @@ typedef struct Apple inline Apple * Constructor_Apple(Apple * __this, int x, int y) { - printf("Apple\n"); __this->mX = (char)x; __this->mY = y; + printf("Apple\n"); return __this; } diff --git a/tests/EduCfrontVtable2Test.cpp b/tests/EduCfrontVtable2Test.cpp new file mode 100644 index 00000000..ef7dded4 --- /dev/null +++ b/tests/EduCfrontVtable2Test.cpp @@ -0,0 +1,47 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +class A { +public: + int a; + virtual void v() { puts("A->v"); } +}; + +class B { +public: + int b; + virtual void w() { puts("B->w"); } +}; + +class C : public A, public B { +public: + int c; + void w() { puts("C->w"); } +}; + +class D : public A, public B { +public: + int d; +}; + +int main() +{ + C c; + + c.w(); + c.v(); + + B* b = &c; + b->w(); + + C* cb = (C*)(b); + cb->v(); + + // + D d; + B* bd = &d; + D* db = (D*)bd; + db->w(); +} + diff --git a/tests/EduCfrontVtable2Test.expect b/tests/EduCfrontVtable2Test.expect new file mode 100644 index 00000000..ec8e6135 --- /dev/null +++ b/tests/EduCfrontVtable2Test.expect @@ -0,0 +1,209 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + + +#include + +typedef struct A +{ + __mptr * __vptrA; + int a; +} A; + +inline void vA(A * __this) +{ + puts("A->v"); +} + +inline A * operatorEqual(A * __this, const A * __rhs) +{ + __this->a = __rhs->a; + return __this; +} + +inline A * operatorEqual(A * __this, A * __rhs) +{ + __this->a = __rhs->a; + return __this; +} + +inline A * Constructor_A(A * __this) +{ + __this->__vptrA = __vtbl_array[0]; + return __this; +} + + +typedef struct B +{ + __mptr * __vptrB; + int b; +} B; + +inline void wB(B * __this) +{ + puts("B->w"); +} + +inline B * operatorEqual(B * __this, const B * __rhs) +{ + __this->b = __rhs->b; + return __this; +} + +inline B * operatorEqual(B * __this, B * __rhs) +{ + __this->b = __rhs->b; + return __this; +} + +inline B * Constructor_B(B * __this) +{ + __this->__vptrB = __vtbl_array[1]; + return __this; +} + + +typedef struct C +{ + __mptr * __vptrA; + int a; + __mptr * __vptrB; + int b; + int c; +} C; + +inline void wC(C * __this) +{ + puts("C->w"); +} + +inline C * operatorEqual(C * __this, const C * __rhs) +{ + __this->a = __rhs->a; + operatorEqual((A *)__this, (A *)__rhs); + __this->b = __rhs->b; + operatorEqual((B *)((char*)__this+16), (B *)((char*)__rhs+16)); + __this->c = __rhs->c; + return __this; +} + +inline C * operatorEqual(C * __this, C * __rhs) +{ + __this->a = __rhs->a; + operatorEqual((A *)__this, (A *)__rhs); + __this->b = __rhs->b; + operatorEqual((B *)((char*)__this+16), (B *)((char*)__rhs+16)); + __this->c = __rhs->c; + return __this; +} + +inline C * Constructor_C(C * __this) +{ + Constructor_A((A *)__this); + Constructor_B((B *)__this); + __this->__vptrA = __vtbl_array[2]; + __this->__vptrB = __vtbl_array[3]; + return __this; +} + + +typedef struct D +{ + __mptr * __vptrA; + int a; + __mptr * __vptrB; + int b; + int d; +} D; + +inline D * operatorEqual(D * __this, const D * __rhs) +{ + __this->a = __rhs->a; + operatorEqual((A *)__this, (A *)__rhs); + __this->b = __rhs->b; + operatorEqual((B *)((char*)__this+16), (B *)((char*)__rhs+16)); + __this->d = __rhs->d; + return __this; +} + +inline D * operatorEqual(D * __this, D * __rhs) +{ + __this->a = __rhs->a; + operatorEqual((A *)__this, (A *)__rhs); + __this->b = __rhs->b; + operatorEqual((B *)((char*)__this+16), (B *)((char*)__rhs+16)); + __this->d = __rhs->d; + return __this; +} + +inline D * Constructor_D(D * __this) +{ + Constructor_A((A *)__this); + Constructor_B((B *)__this); + __this->__vptrA = __vtbl_array[4]; + __this->__vptrB = __vtbl_array[5]; + return __this; +} + + +int __main(void) +{ + C c; + Constructor_C((C *)&c); + (*((void (*)(C *))((&c)->__vptrA[0]).f))((((C *)(char *)(&c)) + ((&c)->__vptrA[0]).d)); + (*((void (*)(A *))((&c)->__vptrA[0]).f))((((A *)(char *)(&c)) + ((&c)->__vptrA[0]).d)); + B * b = (B *)((char*)&c+16); + (*((void (*)(B *))((b)->__vptrB[0]).f))((((B *)(char *)(b)) + ((b)->__vptrB[0]).d)); + C * cb = (C *)((char*)(b)-16); + (*((void (*)(A *))((cb)->__vptrA[0]).f))((((A *)(char *)(cb)) + ((cb)->__vptrA[0]).d)); + D d; + Constructor_D((D *)&d); + B * bd = (B *)((char*)&d+16); + D * db = (D *)((char*)bd-16); + (*((void (*)(B *))((db)->__vptrB[0]).f))((((B *)(char *)(db)) + ((db)->__vptrB[0]).d)); + return 0; + /* d // lifetime ends here */ + /* c // lifetime ends here */ +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_A[1] = {0, 0, (__vptp)vA}; +__mptr __vtbl_B[1] = {0, 0, (__vptp)wB}; +__mptr __vtbl_CA[2] = {{0, 0, (__vptp)vA}, {0, 0, (__vptp)wC}}; +__mptr __vtbl_CB[1] = {-16, 0, (__vptp)wC}; +__mptr __vtbl_DA[1] = {0, 0, (__vptp)vA}; +__mptr __vtbl_DB[1] = {0, 0, (__vptp)wB}; + +__mptr * __vtbl_array[6] = {__vtbl_A, __vtbl_B, __vtbl_CA, __vtbl_CB, __vtbl_DA, __vtbl_DB}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} + diff --git a/tests/EduCfrontVtable3Test.cpp b/tests/EduCfrontVtable3Test.cpp new file mode 100644 index 00000000..2b51c169 --- /dev/null +++ b/tests/EduCfrontVtable3Test.cpp @@ -0,0 +1,33 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +struct A { + double md; + virtual void Fun() { puts("fun a"); } +}; + +struct B : A { + int mX{5}; + void Fun() { printf("fun b: %d\n", mX); } + + virtual void Other() {} +}; + +struct C : B { + int mB{8}; + void Fun() { printf("fun c: %d\n", mB); } + + virtual void Other() {} +}; + +int main() +{ + C b{}; + + b.Fun(); + + A* a{&b}; + a->Fun(); +} + diff --git a/tests/EduCfrontVtable3Test.expect b/tests/EduCfrontVtable3Test.expect new file mode 100644 index 00000000..78d3ea57 --- /dev/null +++ b/tests/EduCfrontVtable3Test.expect @@ -0,0 +1,167 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + + +#include + +typedef struct A +{ + __mptr * __vptrA; + double md; +} A; + +inline void FunA(A * __this) +{ + puts("fun a"); +} + +inline A * operatorEqual(A * __this, const A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * operatorEqual(A * __this, A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * Constructor_A(A * __this) +{ + __this->__vptrA = __vtbl_array[0]; + return __this; +} + + +typedef struct B +{ + __mptr * __vptrA; + double md; + int mX; +} B; + +inline void FunB(B * __this) +{ + printf("fun b: %d\n", __this->mX); +} + +inline void OtherB(B * __this) +{ +} + +inline B * operatorEqual(B * __this, const B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * operatorEqual(B * __this, B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * Constructor_B(B * __this) +{ + Constructor_A((A *)__this); + __this->__vptrA = __vtbl_array[1]; + __this->mX = 5; + return __this; +} + + +typedef struct C +{ + __mptr * __vptrA; + double md; + int mX; + int mB; +} C; + +inline void FunC(C * __this) +{ + printf("fun c: %d\n", __this->mB); +} + +inline void OtherC(C * __this) +{ +} + +inline C * operatorEqual(C * __this, const C * __rhs) +{ + __this->mX = __rhs->mX; + operatorEqual((B *)__this, (B *)__rhs); + __this->mB = __rhs->mB; + return __this; +} + +inline C * operatorEqual(C * __this, C * __rhs) +{ + __this->mX = __rhs->mX; + operatorEqual((B *)__this, (B *)__rhs); + __this->mB = __rhs->mB; + return __this; +} + +inline C * Constructor_C(C * __this) +{ + Constructor_B((B *)__this); + __this->mX = 5; + __this->mB = 8; + return __this; +} + + +int __main(void) +{ + C b; + Constructor_C((C *)&b); + (*((void (*)(C *))((&b)->__vptrA[0]).f))((((C *)(char *)(&b)) + ((&b)->__vptrA[0]).d)); + A * a = (A *)&b; + (*((void (*)(A *))((a)->__vptrA[0]).f))((((A *)(char *)(a)) + ((a)->__vptrA[0]).d)); + return 0; + /* b // lifetime ends here */ +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_A[1] = {0, 0, (__vptp)FunA}; +__mptr __vtbl_B[2] = {{0, 0, (__vptp)FunB}, {0, 0, (__vptp)OtherB}}; +__mptr __vtbl_C[2] = {{0, 0, (__vptp)FunC}, {0, 0, (__vptp)OtherC}}; + +__mptr * __vtbl_array[3] = {__vtbl_A, __vtbl_B, __vtbl_C}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} + diff --git a/tests/EduCfrontVtable4Test.cpp b/tests/EduCfrontVtable4Test.cpp new file mode 100644 index 00000000..484e3dc4 --- /dev/null +++ b/tests/EduCfrontVtable4Test.cpp @@ -0,0 +1,35 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +struct A { + double md; + virtual ~A() { puts("dtor"); } + + virtual void Fun() { puts("fun a"); } +}; + +struct B : A { + int mX{5}; + void Fun() { printf("fun b: %d\n", mX); } + + virtual void Other() {} +}; + +struct C : B { + int mB{8}; + void Fun() { printf("fun c: %d\n", mB); } + + virtual void Other() {} +}; + +int main() +{ + C b{}; + + b.Fun(); + + A* a{&b}; + a->Fun(); +} + diff --git a/tests/EduCfrontVtable4Test.expect b/tests/EduCfrontVtable4Test.expect new file mode 100644 index 00000000..152c6081 --- /dev/null +++ b/tests/EduCfrontVtable4Test.expect @@ -0,0 +1,176 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + + +#include + +typedef struct A +{ + __mptr * __vptrA; + double md; +} A; + +inline void Destructor_A(A * __this) +{ + puts("dtor"); +} + +inline void FunA(A * __this) +{ + puts("fun a"); +} + +inline A * operatorEqual(A * __this, const A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * Constructor_A(A * __this) +{ + __this->__vptrA = __vtbl_array[0]; + return __this; +} + + +typedef struct B +{ + __mptr * __vptrA; + double md; + int mX; +} B; + +inline void FunB(B * __this) +{ + printf("fun b: %d\n", __this->mX); +} + +inline void OtherB(B * __this) +{ +} + +inline B * operatorEqual(B * __this, const B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * operatorEqual(B * __this, B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline void Destructor_B(B * __this) +{ + Destructor_A((A *)__this); +} + +inline B * Constructor_B(B * __this) +{ + Constructor_A((A *)__this); + __this->__vptrA = __vtbl_array[1]; + __this->mX = 5; + return __this; +} + + +typedef struct C +{ + __mptr * __vptrA; + double md; + int mX; + int mB; +} C; + +inline void FunC(C * __this) +{ + printf("fun c: %d\n", __this->mB); +} + +inline void OtherC(C * __this) +{ +} + +inline C * operatorEqual(C * __this, const C * __rhs) +{ + __this->mX = __rhs->mX; + operatorEqual((B *)__this, (B *)__rhs); + __this->mB = __rhs->mB; + return __this; +} + +inline C * operatorEqual(C * __this, C * __rhs) +{ + __this->mX = __rhs->mX; + operatorEqual((B *)__this, (B *)__rhs); + __this->mB = __rhs->mB; + return __this; +} + +inline void Destructor_C(C * __this) +{ + Destructor_B((B *)__this); +} + +inline C * Constructor_C(C * __this) +{ + Constructor_B((B *)__this); + __this->mX = 5; + __this->mB = 8; + return __this; +} + + +int __main(void) +{ + C b; + Constructor_C((C *)&b); + (*((void (*)(C *))((&b)->__vptrA[0]).f))((((C *)(char *)(&b)) + ((&b)->__vptrA[0]).d)); + A * a = (A *)&b; + (*((void (*)(A *))((a)->__vptrA[0]).f))((((A *)(char *)(a)) + ((a)->__vptrA[0]).d)); + return 0; + (*((void (*)(C *))((&b)->__vptrA[0]).f))((((C *)(char *)(&b)) + ((&b)->__vptrA[0]).d)); +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_A[2] = {{0, 0, (__vptp)Destructor_A}, {0, 0, (__vptp)FunA}}; +__mptr __vtbl_B[3] = {{0, 0, (__vptp)Destructor_B}, {0, 0, (__vptp)FunB}, {0, 0, (__vptp)OtherB}}; +__mptr __vtbl_C[3] = {{0, 0, (__vptp)Destructor_C}, {0, 0, (__vptp)FunC}, {0, 0, (__vptp)OtherC}}; + +__mptr * __vtbl_array[3] = {__vtbl_A, __vtbl_B, __vtbl_C}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} + diff --git a/tests/EduCfrontVtable5Test.cpp b/tests/EduCfrontVtable5Test.cpp new file mode 100644 index 00000000..464c5824 --- /dev/null +++ b/tests/EduCfrontVtable5Test.cpp @@ -0,0 +1,30 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +class Base { +public: + virtual ~Base() { puts("~Base"); } +}; + +class Derived : public Base { +public: + ~Derived() { puts("~Derived"); } +}; + +class BaseNonVirtual { +public: + ~BaseNonVirtual() { puts("~BaseNonVirtual"); } +}; + +class DerivedNonVirtual : public BaseNonVirtual { +public: + ~DerivedNonVirtual() { puts("~DerivedNonVirtual"); } +}; + +int main() +{ + Derived d{}; + DerivedNonVirtual nvd; +} + diff --git a/tests/EduCfrontVtable5Test.expect b/tests/EduCfrontVtable5Test.expect new file mode 100644 index 00000000..5a03447b --- /dev/null +++ b/tests/EduCfrontVtable5Test.expect @@ -0,0 +1,132 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + + +#include + +typedef struct Base +{ + __mptr * __vptrBase; +} Base; + +inline void Destructor_Base(Base * __this) +{ + puts("~Base"); +} + +inline Base * operatorEqual(Base * __this, const Base * __rhs) +{ + return __this; +} + +inline Base * Constructor_Base(Base * __this) +{ + __this->__vptrBase = __vtbl_array[0]; + return __this; +} + + +typedef struct Derived +{ + __mptr * __vptrBase; +} Derived; + +inline void Destructor_Derived(Derived * __this) +{ + puts("~Derived"); + Destructor_Base((Base *)__this); +} + +inline Derived * operatorEqual(Derived * __this, const Derived * __rhs) +{ + operatorEqual((Base *)__this, (Base *)__rhs); + return __this; +} + +inline Derived * Constructor_Derived(Derived * __this) +{ + Constructor_Base((Base *)__this); + __this->__vptrBase = __vtbl_array[1]; + return __this; +} + + +typedef struct BaseNonVirtual +{ + char __dummy; +} BaseNonVirtual; + +inline void Destructor_BaseNonVirtual(BaseNonVirtual * __this) +{ + puts("~BaseNonVirtual"); +} + +inline BaseNonVirtual * Constructor_BaseNonVirtual(BaseNonVirtual * __this) +{ + return __this; +} + + +typedef struct DerivedNonVirtual +{ + char __dummy; +} DerivedNonVirtual; + +inline void Destructor_DerivedNonVirtual(DerivedNonVirtual * __this) +{ + puts("~DerivedNonVirtual"); +} + +inline DerivedNonVirtual * Constructor_DerivedNonVirtual(DerivedNonVirtual * __this) +{ + return __this; +} + + +int __main(void) +{ + Derived d; + Constructor_Derived((Derived *)&d); + DerivedNonVirtual nvd; + Constructor_DerivedNonVirtual((DerivedNonVirtual *)&nvd); + return 0; + Destructor_DerivedNonVirtual(&nvd); + (*((void (*)(Derived *))((&d)->__vptrBase[0]).f))((((Derived *)(char *)(&d)) + ((&d)->__vptrBase[0]).d)); +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_Base[1] = {0, 0, (__vptp)Destructor_Base}; +__mptr __vtbl_Derived[1] = {0, 0, (__vptp)Destructor_Derived}; + +__mptr * __vtbl_array[2] = {__vtbl_Base, __vtbl_Derived}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} + diff --git a/tests/EduCfrontVtable6Test.cpp b/tests/EduCfrontVtable6Test.cpp new file mode 100644 index 00000000..edb293b9 --- /dev/null +++ b/tests/EduCfrontVtable6Test.cpp @@ -0,0 +1,29 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +struct A { + double md; +}; + +struct B { + int mX{5}; + + virtual void Fun() { puts("B::Fun"); } +}; + +struct C : A, B { + int mB{8}; + void Fun() { puts("C::Fun"); } +}; + +int main() +{ + C c{}; + + c.Fun(); + + B* b{&c}; + b->Fun(); +} + diff --git a/tests/EduCfrontVtable6Test.expect b/tests/EduCfrontVtable6Test.expect new file mode 100644 index 00000000..181622fc --- /dev/null +++ b/tests/EduCfrontVtable6Test.expect @@ -0,0 +1,145 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + + +#include + +typedef struct A +{ + double md; +} A; + +inline A * operatorEqual(A * __this, const A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * operatorEqual(A * __this, A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + + +typedef struct B +{ + __mptr * __vptrB; + int mX; +} B; + +inline void FunB(B * __this) +{ + puts("B::Fun"); +} + +inline B * operatorEqual(B * __this, const B * __rhs) +{ + __this->mX = __rhs->mX; + return __this; +} + +inline B * operatorEqual(B * __this, B * __rhs) +{ + __this->mX = __rhs->mX; + return __this; +} + +inline B * Constructor_B(B * __this) +{ + __this->__vptrB = __vtbl_array[0]; + __this->mX = 5; + return __this; +} + + +typedef struct C +{ + double md; + __mptr * __vptrB; + int mX; + int mB; +} C; + +inline void FunC(C * __this) +{ + puts("C::Fun"); +} + +inline C * operatorEqual(C * __this, const C * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + operatorEqual((B *)__this, (B *)__rhs); + __this->mB = __rhs->mB; + return __this; +} + +inline C * operatorEqual(C * __this, C * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + operatorEqual((B *)__this, (B *)__rhs); + __this->mB = __rhs->mB; + return __this; +} + +inline C * Constructor_C(C * __this) +{ + Constructor_B((B *)__this); + __this->mX = 5; + __this->__vptrB = __vtbl_array[1]; + __this->mB = 8; + return __this; +} + + +int __main(void) +{ + C c; + Constructor_C((C *)&c); + (*((void (*)(C *))((&c)->__vptrB[0]).f))((((C *)(char *)(&c)) + ((&c)->__vptrB[0]).d)); + B * b = (B *)&c; + (*((void (*)(B *))((b)->__vptrB[0]).f))((((B *)(char *)(b)) + ((b)->__vptrB[0]).d)); + return 0; + /* c // lifetime ends here */ +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_B[1] = {0, 0, (__vptp)FunB}; +__mptr __vtbl_CA[1] = {0, 0, (__vptp)FunC}; + +__mptr * __vtbl_array[2] = {__vtbl_B, __vtbl_CA}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} + diff --git a/tests/EduCfrontVtablePureFuncTest.cpp b/tests/EduCfrontVtablePureFuncTest.cpp new file mode 100644 index 00000000..a05d85f5 --- /dev/null +++ b/tests/EduCfrontVtablePureFuncTest.cpp @@ -0,0 +1,28 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +struct A { + double md; + virtual void Fun() = 0; + + A() { Fun(); } +}; + +struct B : A { + int mX{5}; + void Fun() { printf("fun b: %d\n", mX); } + + virtual void Other() {} +}; + +int main() +{ + B b{}; + + b.Fun(); + + A* a{&b}; + a->Fun(); +} + diff --git a/tests/EduCfrontVtablePureFuncTest.expect b/tests/EduCfrontVtablePureFuncTest.expect new file mode 100644 index 00000000..3c250a58 --- /dev/null +++ b/tests/EduCfrontVtablePureFuncTest.expect @@ -0,0 +1,124 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +#include // abort +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + +extern "C" void __cxa_pure_virtual() { abort(); } + +#include + +typedef struct A +{ + __mptr * __vptrA; + double md; +} A; + +void FunA(A * __this); + +inline A * Constructor_A(A * __this) +{ + __this->__vptrA = __vtbl_array[0]; + (*((void (*)(A *))((__this)->__vptrA[0]).f))((((A *)(char *)(__this)) + ((__this)->__vptrA[0]).d)); + return __this; +} + +inline A * operatorEqual(A * __this, const A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * operatorEqual(A * __this, A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + + +typedef struct B +{ + __mptr * __vptrA; + double md; + int mX; +} B; + +inline void FunB(B * __this) +{ + printf("fun b: %d\n", __this->mX); +} + +inline void OtherB(B * __this) +{ +} + +inline B * operatorEqual(B * __this, const B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * operatorEqual(B * __this, B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * Constructor_B(B * __this) +{ + Constructor_A((A *)__this); + __this->__vptrA = __vtbl_array[1]; + __this->mX = 5; + return __this; +} + + +int __main(void) +{ + B b; + Constructor_B((B *)&b); + (*((void (*)(B *))((&b)->__vptrA[0]).f))((((B *)(char *)(&b)) + ((&b)->__vptrA[0]).d)); + A * a = (A *)&b; + (*((void (*)(A *))((a)->__vptrA[0]).f))((((A *)(char *)(a)) + ((a)->__vptrA[0]).d)); + return 0; + /* b // lifetime ends here */ +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_A[1] = {0, 0, (__vptp)__cxa_pure_virtual}; +__mptr __vtbl_B[2] = {{0, 0, (__vptp)FunB}, {0, 0, (__vptp)OtherB}}; + +__mptr * __vtbl_array[2] = {__vtbl_A, __vtbl_B}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} + diff --git a/tests/EduCfrontVtableTest.cpp b/tests/EduCfrontVtableTest.cpp new file mode 100644 index 00000000..dff39be6 --- /dev/null +++ b/tests/EduCfrontVtableTest.cpp @@ -0,0 +1,26 @@ +// cmdlineinsights:-edu-show-cfront + +#include + +struct A { + double md; + virtual void Fun() { puts("fun a"); } +}; + +struct B : A { + int mX{5}; + void Fun() { printf("fun b: %d\n", mX); } + + virtual void Other() {} +}; + +int main() +{ + B b{}; + + b.Fun(); + + A* a{&b}; + a->Fun(); +} + diff --git a/tests/EduCfrontVtableTest.expect b/tests/EduCfrontVtableTest.expect new file mode 100644 index 00000000..be657796 --- /dev/null +++ b/tests/EduCfrontVtableTest.expect @@ -0,0 +1,124 @@ +/************************************************************************************* + * NOTE: This an educational hand-rolled transformation. Things can be incorrect or * + * buggy. * + *************************************************************************************/ +void __cxa_start(void); +void __cxa_atexit(void); +typedef int (*__vptp)(); + +struct __mptr +{ + short d; + short i; + __vptp f; +}; + +extern struct __mptr* __vtbl_array[]; + + +#include + +typedef struct A +{ + __mptr * __vptrA; + double md; +} A; + +inline void FunA(A * __this) +{ + puts("fun a"); +} + +inline A * operatorEqual(A * __this, const A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * operatorEqual(A * __this, A * __rhs) +{ + __this->md = __rhs->md; + return __this; +} + +inline A * Constructor_A(A * __this) +{ + __this->__vptrA = __vtbl_array[0]; + return __this; +} + + +typedef struct B +{ + __mptr * __vptrA; + double md; + int mX; +} B; + +inline void FunB(B * __this) +{ + printf("fun b: %d\n", __this->mX); +} + +inline void OtherB(B * __this) +{ +} + +inline B * operatorEqual(B * __this, const B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * operatorEqual(B * __this, B * __rhs) +{ + __this->md = __rhs->md; + operatorEqual((A *)__this, (A *)__rhs); + __this->mX = __rhs->mX; + return __this; +} + +inline B * Constructor_B(B * __this) +{ + Constructor_A((A *)__this); + __this->__vptrA = __vtbl_array[1]; + __this->mX = 5; + return __this; +} + + +int __main(void) +{ + B b; + Constructor_B((B *)&b); + (*((void (*)(B *))((&b)->__vptrA[0]).f))((((B *)(char *)(&b)) + ((&b)->__vptrA[0]).d)); + A * a = (A *)&b; + (*((void (*)(A *))((a)->__vptrA[0]).f))((((A *)(char *)(a)) + ((a)->__vptrA[0]).d)); + return 0; + /* b // lifetime ends here */ +} + +int main(void) +{ + __cxa_start(); + int ret = __main(); + __cxa_atexit(); + return ret; + /* ret // lifetime ends here */ +} + +__mptr __vtbl_A[1] = {0, 0, (__vptp)FunA}; +__mptr __vtbl_B[2] = {{0, 0, (__vptp)FunB}, {0, 0, (__vptp)OtherB}}; + +__mptr * __vtbl_array[2] = {__vtbl_A, __vtbl_B}; + +void __cxa_start(void) +{ +} + +void __cxa_atexit(void) +{ +} +