From: Anders Carlsson Date: Thu, 11 Feb 2010 08:02:13 +0000 (+0000) Subject: Check in the beginnings of my new vtable layout builder idea. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=824d7ea07a4e9208925daa6ae9289fb2b39bce9f;p=clang Check in the beginnings of my new vtable layout builder idea. Right now, it's off by default but can be tested by passing -fdump-vtable-layouts to clang -cc1. This option will cause all vtables that will normally be emitted as part of codegen to also be dumped using the new layout code. I've also added a very simple new vtable layout test case. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@95865 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index 1172206e92..d909f83e74 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -97,7 +97,9 @@ public: // operators unsigned ElideConstructors : 1; // Whether C++ copy constructors should be // elided if possible. - unsigned CatchUndefined :1; // Generate code to check for undefined ops. + unsigned CatchUndefined : 1; // Generate code to check for undefined ops. + unsigned DumpVtableLayouts : 1; // Dump the layouts of all the emitted + // vtables. private: unsigned GC : 2; // Objective-C Garbage Collection modes. We // declare this enum as unsigned because MSVC @@ -168,6 +170,7 @@ public: CharIsSigned = 1; ShortWChar = 0; CatchUndefined = 0; + DumpVtableLayouts = 0; } GCMode getGCMode() const { return (GCMode) GC; } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index a235fa7892..eac6e0761f 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -117,6 +117,8 @@ def fno_merge_all_constants : Flag<"-fno-merge-all-constants">, HelpText<"Disallow merging of constants.">; def fno_threadsafe_statics : Flag<"-fno-threadsafe-statics">, HelpText<"Do not emit code to make initialization of local statics thread safe">; +def fdump_vtable_layouts : Flag<"-fdump-vtable-layouts">, + HelpText<"Dump the layouts of all vtables that will be emitted in a translation unit">; def masm_verbose : Flag<"-masm-verbose">, HelpText<"Generate verbose assembly output">; def mcode_model : Separate<"-mcode-model">, diff --git a/lib/CodeGen/CGVtable.cpp b/lib/CodeGen/CGVtable.cpp index 8b90f2848c..5d3944f6dd 100644 --- a/lib/CodeGen/CGVtable.cpp +++ b/lib/CodeGen/CGVtable.cpp @@ -16,13 +16,207 @@ #include "clang/AST/CXXInheritance.h" #include "clang/AST/RecordLayout.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/Support/Format.h" #include using namespace clang; using namespace CodeGen; namespace { + +/// VtableComponent - Represents a single component in a vtable. +class VtableComponent { +public: + enum Kind { + CK_VCallOffset, + CK_VBaseOffset, + CK_OffsetToTop, + CK_RTTI, + CK_VFunctionPointer + }; + + /// dump - Dump the contents of this component to the given stream. + void dump(llvm::raw_ostream &Out); + + static VtableComponent MakeOffsetToTop(int64_t Offset) { + return VtableComponent(CK_OffsetToTop, Offset); + } + + static VtableComponent MakeRTTI(const CXXRecordDecl *RD) { + return VtableComponent(CK_RTTI, reinterpret_cast(RD)); + } + + static VtableComponent MakeFunction(const CXXMethodDecl *MD) { + assert(!isa(MD) && + "Don't know how to handle dtors yet!"); + + return VtableComponent(CK_VFunctionPointer, + reinterpret_cast(MD)); + } + + /// getKind - Get the kind of this vtable component. + Kind getKind() const { + return (Kind)(Value & 0x7); + } + + int64_t getOffsetToTop() const { + assert(getKind() == CK_OffsetToTop && "Invalid component kind!"); + + return getOffset(); + } + + const CXXRecordDecl *getRTTIDecl() const { + assert(getKind() == CK_RTTI && "Invalid component kind!"); + + return reinterpret_cast(getPointer()); + } + + const CXXMethodDecl *getFunctionDecl() const { + assert(getKind() == CK_VFunctionPointer); + + return reinterpret_cast(getPointer()); + } + +private: + VtableComponent(Kind ComponentKind, int64_t Offset) { + assert((ComponentKind == CK_VCallOffset || + ComponentKind == CK_VBaseOffset || + ComponentKind == CK_OffsetToTop) && "Invalid component kind!"); + assert(Offset <= ((1LL << 56) - 1) && "Offset is too big!"); + + Value = ((Offset << 3) | ComponentKind); + } + + VtableComponent(Kind ComponentKind, uintptr_t Ptr) { + assert((ComponentKind == CK_RTTI || + ComponentKind == CK_VFunctionPointer) && + "Invalid component kind!"); + + assert((Ptr & 7) == 0 && "Pointer not sufficiently aligned!"); + + Value = Ptr | ComponentKind; + } + + int64_t getOffset() const { + assert((getKind() == CK_VCallOffset || getKind() == CK_VBaseOffset || + getKind() == CK_OffsetToTop) && "Invalid component kind!"); + + return Value >> 3; + } + + uintptr_t getPointer() const { + assert((getKind() == CK_RTTI || getKind() == CK_VFunctionPointer) && + "Invalid component kind!"); + + + return static_cast(Value & ~7ULL); + } + + /// The kind is stored in the lower 3 bits of the value. For offsets, we + /// make use of the facts that classes can't be larger than 2^55 bytes, + /// so we store the offset in the lower part of the 61 bytes that remain. + /// (The reason that we're not simply using a PointerIntPair here is that we + /// need the offsets to be 64-bit, even when on a 32-bit machine). + int64_t Value; +}; + +/// VtableBuilder - Class for building vtable layout information. class VtableBuilder { + /// MostDerivedClass - The most derived class for which we're building this + /// vtable. + const CXXRecordDecl *MostDerivedClass; + + /// Context - The ASTContext which we will use for layout information. + const ASTContext &Context; + + /// Components - The components of the vtable being built. + llvm::SmallVector Components; + + /// layoutSimpleVtable - A test function that will layout very simple vtables + /// without any bases. Just used for testing for now. + void layoutSimpleVtable(const CXXRecordDecl *RD); + +public: + VtableBuilder(const CXXRecordDecl *MostDerivedClass) + : MostDerivedClass(MostDerivedClass), + Context(MostDerivedClass->getASTContext()) { + + layoutSimpleVtable(MostDerivedClass); + } + + /// dumpLayout - Dump the vtable layout. + void dumpLayout(llvm::raw_ostream&); + +}; + +void VtableBuilder::layoutSimpleVtable(const CXXRecordDecl *RD) { + assert(!RD->getNumBases() && + "We don't support layout for vtables with bases right now!"); + + // First, add the offset to top. + Components.push_back(VtableComponent::MakeOffsetToTop(0)); + + // Next, add the RTTI. + Components.push_back(VtableComponent::MakeRTTI(RD)); + + // Now go through all virtual member functions and add them. + for (CXXRecordDecl::method_iterator I = RD->method_begin(), + E = RD->method_end(); I != E; ++I) { + const CXXMethodDecl *MD = *I; + + if (!MD->isVirtual()) + continue; + + // Add the function. + Components.push_back(VtableComponent::MakeFunction(MD)); + } +} + +/// dumpLayout - Dump the vtable layout. +void VtableBuilder::dumpLayout(llvm::raw_ostream& Out) { + + Out << "Vtable for '" << MostDerivedClass->getQualifiedNameAsString(); + Out << "' (" << Components.size() << " entries).\n"; + + for (unsigned I = 0, E = Components.size(); I != E; ++I) { + Out << llvm::format("%4d | ", I); + + const VtableComponent &Component = Components[I]; + + // Dump the component. + switch (Component.getKind()) { + // FIXME: Remove this default case. + default: + assert(false && "Unhandled component kind!"); + break; + + case VtableComponent::CK_OffsetToTop: + Out << "offset_to_top (" << Component.getOffsetToTop() << ")"; + break; + + case VtableComponent::CK_RTTI: + Out << Component.getRTTIDecl()->getQualifiedNameAsString() << " RTTI"; + break; + + case VtableComponent::CK_VFunctionPointer: { + const CXXMethodDecl *MD = Component.getFunctionDecl(); + + Out << MD->getQualifiedNameAsString(); + + break; + } + + } + + Out << '\n'; + } + +} + +} + +namespace { +class OldVtableBuilder { public: /// Index_t - Vtable index type. typedef uint64_t Index_t; @@ -383,7 +577,7 @@ private: } public: - VtableBuilder(const CXXRecordDecl *MostDerivedClass, + OldVtableBuilder(const CXXRecordDecl *MostDerivedClass, const CXXRecordDecl *l, uint64_t lo, CodeGenModule &cgm, bool build, CGVtableInfo::AddressPointsMapTy& AddressPoints) : BuildVtable(build), MostDerivedClass(MostDerivedClass), LayoutClass(l), @@ -978,7 +1172,7 @@ TypeConversionRequiresAdjustment(ASTContext &Ctx, return TypeConversionRequiresAdjustment(Ctx, DerivedDecl, BaseDecl); } -bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, +bool OldVtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, Index_t OverrideOffset, Index_t Offset, int64_t CurrentVBaseOffset) { const CXXMethodDecl *MD = cast(GD.getDecl()); @@ -1105,7 +1299,7 @@ bool VtableBuilder::OverrideMethod(GlobalDecl GD, bool MorallyVirtual, return false; } -void VtableBuilder::AppendMethodsToVtable() { +void OldVtableBuilder::AppendMethodsToVtable() { if (!BuildVtable) { VtableComponents.insert(VtableComponents.end(), Methods.size(), (llvm::Constant *)0); @@ -1332,12 +1526,12 @@ CGVtableInfo::getAdjustments(GlobalDecl GD) { return 0; AddressPointsMapTy AddressPoints; - VtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); + OldVtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); D1(printf("vtable %s\n", RD->getNameAsCString())); b.GenerateVtableForBase(RD); b.GenerateVtableForVBases(RD); - for (VtableBuilder::SavedAdjustmentsVectorTy::iterator + for (OldVtableBuilder::SavedAdjustmentsVectorTy::iterator i = b.getSavedAdjustments().begin(), e = b.getSavedAdjustments().end(); i != e; i++) SavedAdjustments[i->first].push_back(i->second); @@ -1361,7 +1555,7 @@ int64_t CGVtableInfo::getVirtualBaseOffsetIndex(const CXXRecordDecl *RD, // FIXME: This seems expensive. Can we do a partial job to get // just this data. AddressPointsMapTy AddressPoints; - VtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); + OldVtableBuilder b(RD, RD, 0, CGM, false, AddressPoints); D1(printf("vtable %s\n", RD->getNameAsCString())); b.GenerateVtableForBase(RD); b.GenerateVtableForVBases(RD); @@ -1404,8 +1598,8 @@ CGVtableInfo::GenerateVtable(llvm::GlobalVariable::LinkageTypes Linkage, llvm::GlobalVariable *GV = CGM.getModule().getGlobalVariable(Name); if (GV == 0 || CGM.getVtableInfo().AddressPoints[LayoutClass] == 0 || GV->isDeclaration()) { - VtableBuilder b(RD, LayoutClass, Offset, CGM, GenerateDefinition, - AddressPoints); + OldVtableBuilder b(RD, LayoutClass, Offset, CGM, GenerateDefinition, + AddressPoints); D1(printf("vtable %s\n", RD->getNameAsCString())); // First comes the vtables for all the non-virtual bases... @@ -1438,6 +1632,12 @@ CGVtableInfo::GenerateVtable(llvm::GlobalVariable::LinkageTypes Linkage, } } + if (GenerateDefinition && CGM.getLangOptions().DumpVtableLayouts) { + VtableBuilder Builder(RD); + + Builder.dumpLayout(llvm::errs()); + } + return GV; } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 2a63cda050..7287d429b4 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1166,7 +1166,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, if (Args.hasArg(OPT_fno_lax_vector_conversions)) Opts.LaxVectorConversions = 0; if (Args.hasArg(OPT_fno_threadsafe_statics)) - Opts.ThreadsafeStatics = 0; + Opts.ThreadsafeStatics = 0; Opts.Exceptions = Args.hasArg(OPT_fexceptions); Opts.RTTI = !Args.hasArg(OPT_fno_rtti); Opts.Blocks = Args.hasArg(OPT_fblocks); @@ -1193,6 +1193,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); Opts.SjLjExceptions = Args.hasArg(OPT_fsjlj_exceptions); Opts.Static = Args.hasArg(OPT_static_define); + Opts.DumpVtableLayouts = Args.hasArg(OPT_fdump_vtable_layouts); Opts.OptimizeSize = 0; // FIXME: Eliminate this dependency. diff --git a/test/CodeGenCXX/vtable-layout.cpp b/test/CodeGenCXX/vtable-layout.cpp new file mode 100644 index 0000000000..6bfa025bea --- /dev/null +++ b/test/CodeGenCXX/vtable-layout.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm-only -fdump-vtable-layouts 2>&1 | FileCheck %s +namespace Test1 { + +// CHECK: Vtable for 'Test1::A' (3 entries). +// CHECK-NEXT: 0 | offset_to_top (0) +// CHECK-NEXT: 1 | Test1::A RTTI +// CHECK-NEXT: 2 | Test1::A::f +struct A { + virtual void f(); +}; + +void A::f() { } + +} +