https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/185655
We are currently only emitting Vtables that have an 'immediate' need to emit. There rest, we are supposed to add to a list and emit at the end of the translation unit if necessary. This patch implements that infrastructure. The test added is from classic-codegen and came in at the same time as the deferred vtable emission over there, and only works with deferred vtable emission, and while it does test the deferred emission, tests quite a bit more than that. AND since it came in with the same functionality in classic codegen, seemed to make sense to come in here too. >From 698e7ac2217a5dd6a9ec808555e78658cee88f78 Mon Sep 17 00:00:00 2001 From: erichkeane <[email protected]> Date: Wed, 4 Mar 2026 07:30:40 -0800 Subject: [PATCH] [CIR] Implement deferred V-Table emission We are currently only emitting Vtables that have an 'immediate' need to emit. There rest, we are supposed to add to a list and emit at the end of the translation unit if necessary. This patch implements that infrastructure. The test added is from classic-codegen and came in at the same time as the deferred vtable emission over there, and only works with deferred vtable emission, and while it does test the deferred emission, tests quite a bit more than that. AND since it came in with the same functionality in classic codegen, seemed to make sense to come in here too. --- clang/include/clang/CIR/MissingFeatures.h | 1 - clang/lib/CIR/CodeGen/CIRGenCXXABI.h | 5 + clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp | 149 +++++++++- clang/lib/CIR/CodeGen/CIRGenModule.cpp | 13 +- clang/lib/CIR/CodeGen/CIRGenModule.h | 21 ++ clang/lib/CIR/CodeGen/CIRGenVTables.cpp | 62 +++++ clang/test/CIR/CodeGen/vbase.cpp | 5 +- clang/test/CIR/CodeGenCXX/vtable-linkage.cpp | 258 ++++++++++++++++++ .../CIR/CodeGenCXX/vtable-virt-thunk-adj.cpp | 75 +++++ 9 files changed, 579 insertions(+), 10 deletions(-) create mode 100644 clang/test/CIR/CodeGenCXX/vtable-linkage.cpp create mode 100644 clang/test/CIR/CodeGenCXX/vtable-virt-thunk-adj.cpp diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 70a3dc3bdbfa5..7d554d91b7bf8 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -151,7 +151,6 @@ struct MissingFeatures { // Various handling of deferred processing in CIRGenModule. static bool cgmRelease() { return false; } - static bool deferredVtables() { return false; } static bool deferredFuncDecls() { return false; } // CXXABI diff --git a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h index c59185f5a14b0..1e5bd990b0737 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCXXABI.h +++ b/clang/lib/CIR/CodeGen/CIRGenCXXABI.h @@ -127,6 +127,11 @@ class CIRGenCXXABI { virtual void emitRethrow(CIRGenFunction &cgf, bool isNoReturn) = 0; virtual void emitThrow(CIRGenFunction &cgf, const CXXThrowExpr *e) = 0; + /// Determine whether it's possible to emit a vtable for \p RD, even + /// though we do not know that the vtable has been marked as used by semantic + /// analysis. + virtual bool canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const = 0; + virtual void emitBadCastCall(CIRGenFunction &cgf, mlir::Location loc) = 0; virtual void emitBeginCatch(CIRGenFunction &cgf, diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index b347c15e888a0..6db3c1d3902d1 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -105,6 +105,10 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { const CXXDestructorDecl *dtor, CXXDtorType dtorType, Address thisAddr, DeleteOrMemberCallExpr e) override; + + bool canSpeculativelyEmitVTable(const CXXRecordDecl *RD) const override; + bool canSpeculativelyEmitVTableAsBaseClass(const CXXRecordDecl *RD) const; + mlir::Value getVTableAddressPoint(BaseSubobject base, const CXXRecordDecl *vtableClass) override; mlir::Value getVTableAddressPointInStructorWithVTT( @@ -221,6 +225,10 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI { /// the current ABI. RTTIUniquenessKind classifyRTTIUniqueness(QualType canTy, cir::GlobalLinkageKind linkage) const; + +private: + bool hasAnyUnusedVirtualInlineFunction(const CXXRecordDecl *rd) const; + bool isVTableHidden(const CXXRecordDecl *rd) const; }; } // namespace @@ -1789,7 +1797,7 @@ cir::GlobalOp CIRGenItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *rd, return vtable; // Queue up this vtable for possible deferred emission. - assert(!cir::MissingFeatures::deferredVtables()); + cgm.addDeferredVTable(rd); SmallString<256> name; llvm::raw_svector_ostream out(name); @@ -2575,6 +2583,127 @@ void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf, cgf.emitAutoVarCleanups(var); } +bool CIRGenItaniumCXXABI::hasAnyUnusedVirtualInlineFunction( + const CXXRecordDecl *rd) const { + const auto &vtableLayout = cgm.getItaniumVTableContext().getVTableLayout(rd); + + for (const auto &vtableComponent : vtableLayout.vtable_components()) { + // Skip empty slot. + if (!vtableComponent.isUsedFunctionPointerKind()) + continue; + + const CXXMethodDecl *method = vtableComponent.getFunctionDecl(); + const FunctionDecl *fd = method->getDefinition(); + const bool isInlined = + method->getCanonicalDecl()->isInlined() || (fd && fd->isInlined()); + if (!isInlined) + continue; + + StringRef name = cgm.getMangledName( + vtableComponent.getGlobalDecl(/*HasVectorDeletingDtors=*/false)); + auto entry = dyn_cast_or_null<cir::GlobalOp>( + mlir::SymbolTable::lookupSymbolIn(cgm.getModule(), name)); + // This checks if virtual inline function has already been emitted. + // Note that it is possible that this inline function would be emitted + // after trying to emit vtable speculatively. Because of this we do + // an extra pass after emitting all deferred vtables to find and emit + // these vtables opportunistically. + if (!entry || entry.isDeclaration()) + return true; + } + return false; +} + +bool CIRGenItaniumCXXABI::isVTableHidden(const CXXRecordDecl *rd) const { + const auto &vtableLayout = cgm.getItaniumVTableContext().getVTableLayout(rd); + + for (const auto &vtableComponent : vtableLayout.vtable_components()) { + if (vtableComponent.isRTTIKind()) { + const CXXRecordDecl *rttiDecl = vtableComponent.getRTTIDecl(); + if (rttiDecl->getVisibility() == Visibility::HiddenVisibility) + return true; + } else if (vtableComponent.isUsedFunctionPointerKind()) { + const CXXMethodDecl *method = vtableComponent.getFunctionDecl(); + if (method->getVisibility() == Visibility::HiddenVisibility && + !method->isDefined()) + return true; + } + } + return false; +} + +bool CIRGenItaniumCXXABI::canSpeculativelyEmitVTableAsBaseClass( + const CXXRecordDecl *rd) const { + // We don't emit available_externally vtables if we are in -fapple-kext mode + // because kext mode does not permit devirtualization. + if (cgm.getLangOpts().AppleKext) + return false; + + // If the vtable is hidden then it is not safe to emit an available_externally + // copy of vtable. + if (isVTableHidden(rd)) + return false; + + if (cgm.getCodeGenOpts().ForceEmitVTables) + return true; + + // A speculative vtable can only be generated if all virtual inline functions + // defined by this class are emitted. The vtable in the final program contains + // for each virtual inline function not used in the current TU a function that + // is equivalent to the unused function. The function in the actual vtable + // does not have to be declared under the same symbol (e.g., a virtual + // destructor that can be substituted with its base class's destructor). Since + // inline functions are emitted lazily and this emissions does not account for + // speculative emission of a vtable, we might generate a speculative vtable + // with references to inline functions that are not emitted under that name. + // This can lead to problems when devirtualizing a call to such a function, + // that result in linking errors. Hence, if there are any unused virtual + // inline function, we cannot emit the speculative vtable. + // FIXME we can still emit a copy of the vtable if we + // can emit definition of the inline functions. + if (hasAnyUnusedVirtualInlineFunction(rd)) + return false; + + // For a class with virtual bases, we must also be able to speculatively + // emit the VTT, because CodeGen doesn't have separate notions of "can emit + // the vtable" and "can emit the VTT". For a base subobject, this means we + // need to be able to emit non-virtual base vtables. + if (rd->getNumVBases()) { + for (const auto &b : rd->bases()) { + auto *brd = b.getType()->getAsCXXRecordDecl(); + assert(brd && "no class for base specifier"); + if (b.isVirtual() || !brd->isDynamicClass()) + continue; + if (!canSpeculativelyEmitVTableAsBaseClass(brd)) + return false; + } + } + + return true; +} + +bool CIRGenItaniumCXXABI::canSpeculativelyEmitVTable( + const CXXRecordDecl *rd) const { + if (!canSpeculativelyEmitVTableAsBaseClass(rd)) + return false; + + if (rd->shouldEmitInExternalSource()) + return false; + + // For a complete-object vtable (or more specifically, for the VTT), we need + // to be able to speculatively emit the vtables of all dynamic virtual bases. + for (const auto &b : rd->vbases()) { + auto *brd = b.getType()->getAsCXXRecordDecl(); + assert(brd && "no class for base specifier"); + if (!brd->isDynamicClass()) + continue; + if (!canSpeculativelyEmitVTableAsBaseClass(brd)) + return false; + } + + return true; +} + static mlir::Value performTypeAdjustment(CIRGenFunction &cgf, Address initialPtr, const CXXRecordDecl *unadjustedClass, @@ -2598,8 +2727,22 @@ static mlir::Value performTypeAdjustment(CIRGenFunction &cgf, // Perform the virtual adjustment if we have one. mlir::Value resultPtr; if (virtualAdjustment) { - cgf.cgm.errorNYI("virtual adjustment in thunk"); - resultPtr = v; + mlir::Value vtablePtr = cgf.getVTablePtr( + loc, Address(v, clang::CharUnits::One()), unadjustedClass); + vtablePtr = builder.createBitcast(vtablePtr, i8PtrTy); + + mlir::Value offset; + mlir::Value offsetPtr = + cir::PtrStrideOp::create(builder, loc, i8PtrTy, vtablePtr, + builder.getSInt64(virtualAdjustment, loc)); + if (cgf.cgm.getItaniumVTableContext().isRelativeLayout()) { + cgf.cgm.errorNYI("virtual adjustment for relative layout vtables"); + } else { + offset = builder.createAlignedLoad(loc, cgf.ptrDiffTy, offsetPtr, + cgf.getPointerAlign()); + } + + resultPtr = cir::PtrStrideOp::create(builder, loc, i8PtrTy, v, offset); } else { resultPtr = v; } diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 1b4939b4741bb..10168792cd730 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -350,7 +350,13 @@ void CIRGenModule::emitDeferred() { // static function, iterate until no changes are made. assert(!cir::MissingFeatures::openMP()); - assert(!cir::MissingFeatures::deferredVtables()); + + emitDeferredVTables(); + // Emitting a vtable doesn't directly cause more vtables to + // become deferred, although it can cause functions to be + // emitted that then need those vtables. + assert(deferredVTables.empty()); + assert(!cir::MissingFeatures::cudaSupport()); // Stop if we're out of both deferred vtables and deferred declarations. @@ -368,9 +374,9 @@ void CIRGenModule::emitDeferred() { // If we found out that we need to emit more decls, do that recursively. // This has the advantage that the decls are emitted in a DFS and related // ones are close together, which is convenient for testing. - if (!deferredDeclsToEmit.empty()) { + if (!deferredVTables.empty() || !deferredDeclsToEmit.empty()) { emitDeferred(); - assert(deferredDeclsToEmit.empty()); + assert(deferredVTables.empty() && deferredDeclsToEmit.empty()); } } } @@ -2787,6 +2793,7 @@ CIRGenModule::getGlobalVisibilityAttrFromDecl(const Decl *decl) { void CIRGenModule::release() { emitDeferred(); + emitVTablesOpportunistically(); applyReplacements(); theModule->setAttr(cir::CIRDialect::getModuleLevelAsmAttrName(), diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h index ac1699b3b8bfe..24715559445f6 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.h +++ b/clang/lib/CIR/CodeGen/CIRGenModule.h @@ -102,6 +102,12 @@ class CIRGenModule : public CIRGenTypeCache { llvm::DenseSet<clang::GlobalDecl> diagnosedConflictingDefinitions; + /// A queue of (optional) vtables to consider emitting. + std::vector<const CXXRecordDecl*> deferredVTables; + + /// A queue of (optional) vtables that may be emitted opportunistically. + std::vector<const CXXRecordDecl *> opportunisticVTables; + void createCUDARuntime(); /// A helper for constructAttributeList that handles return attributes. @@ -489,6 +495,11 @@ class CIRGenModule : public CIRGenTypeCache { void emitExplicitCastExprType(const ExplicitCastExpr *e, CIRGenFunction *cgf = nullptr); + void addDeferredVTable(const CXXRecordDecl *rd) { + deferredVTables.push_back(rd); + } + + /// Emit code for a single global function or variable declaration. Forward /// declarations are emitted lazily. void emitGlobal(clang::GlobalDecl gd); @@ -675,6 +686,16 @@ class CIRGenModule : public CIRGenTypeCache { /// Emit any needed decls for which code generation was deferred. void emitDeferred(); + bool shouldOpportunisticallyEmitVTables(); + /// Emit any vtables which we deferred and still have a use for. + void emitDeferredVTables(); + + /// Try to emit external vtables as available_externally if they have emitted + /// all inlined virtual functions. It runs after EmitDeferred() and therefore + /// is not allowed to create new references to things that need to be emitted + /// lazily. + void emitVTablesOpportunistically(); + /// Helper for `emitDeferred` to apply actual codegen. void emitGlobalDecl(const clang::GlobalDecl &d); diff --git a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp index 00f3adfb1757f..328529c6658ca 100644 --- a/clang/lib/CIR/CodeGen/CIRGenVTables.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenVTables.cpp @@ -940,3 +940,65 @@ void CIRGenVTables::emitThunks(GlobalDecl gd) { for (const ThunkInfo &thunk : *thunkInfoVector) maybeEmitThunk(gd, thunk, /*forVTable=*/false); } + +static bool shouldEmitAvailableExternallyVTable(const CIRGenModule &cgm, + const CXXRecordDecl *rd) { + return cgm.getCodeGenOpts().OptimizationLevel > 0 && + cgm.getCXXABI().canSpeculativelyEmitVTable(rd); +} + +/// Given that we're currently at the end of the translation unit, and +/// we've emitted a reference to the vtable for this class, should +/// we define that vtable? +static bool shouldEmitVTableAtEndOfTranslationUnit(CIRGenModule &cgm, + const CXXRecordDecl *rd) { + // If vtable is internal then it has to be done. + if (!cgm.getVTables().isVTableExternal(rd)) + return true; + + // If it's external then maybe we will need it as available_externally. + return shouldEmitAvailableExternallyVTable(cgm, rd); +} + +/// Given that at some point we emitted a reference to one or more +/// vtables, and that we are now at the end of the translation unit, +/// decide whether we should emit them. +void CIRGenModule::emitDeferredVTables() { +#ifndef NDEBUG + // Remember the size of DeferredVTables, because we're going to assume + // that this entire operation doesn't modify it. + size_t savedSize = deferredVTables.size(); +#endif + for (const CXXRecordDecl *rd : deferredVTables) + if (shouldEmitVTableAtEndOfTranslationUnit(*this, rd)) + vtables.generateClassData(rd); + else if (shouldOpportunisticallyEmitVTables()) + opportunisticVTables.push_back(rd); + + assert(savedSize == deferredVTables.size() && + "deferred extra vtables during vtable emission?"); + deferredVTables.clear(); +} + +void CIRGenModule::emitVTablesOpportunistically() { + // Try to emit external vtables as available_externally if they have emitted + // all inlined virtual functions. It runs after EmitDeferred() and therefore + // is not allowed to create new references to things that need to be emitted + // lazily. Note that it also uses fact that we eagerly emitting RTTI. + + assert( + (opportunisticVTables.empty() || shouldOpportunisticallyEmitVTables()) && + "Only emit opportunistic vtables with optimizations"); + + for (const CXXRecordDecl *rd : opportunisticVTables) { + assert(getVTables().isVTableExternal(rd) && + "This queue should only contain external vtables"); + if (getCXXABI().canSpeculativelyEmitVTable(rd)) + vtables.generateClassData(rd); + } + opportunisticVTables.clear(); +} + +bool CIRGenModule::shouldOpportunisticallyEmitVTables() { + return codeGenOpts.OptimizationLevel > 0; +} diff --git a/clang/test/CIR/CodeGen/vbase.cpp b/clang/test/CIR/CodeGen/vbase.cpp index ff02281abcc33..3ae8d192c798a 100644 --- a/clang/test/CIR/CodeGen/vbase.cpp +++ b/clang/test/CIR/CodeGen/vbase.cpp @@ -51,9 +51,8 @@ void ppp() { B b; } // Note: OGCG speculatively emits the VTT and VTables. This is not yet implemented in CIR. // Vtable definition for B -// CIR: cir.global "private" external @_ZTV1B - -// LLVM: @_ZTV1B = external global { [3 x ptr] } +// CIR: cir.global "private" {{.*}}@_ZTV1B = #cir.vtable<{#cir.const_array<[#cir.ptr<12 : i64> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1B> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !rec_anon_struct3 {alignment = 8 : i64} +// LLVM: @_ZTV1B = linkonce_odr global { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8 // OGCG: @_ZTV1B = linkonce_odr unnamed_addr constant { [3 x ptr] } { [3 x ptr] [ptr inttoptr (i64 12 to ptr), ptr null, ptr @_ZTI1B] }, comdat, align 8 diff --git a/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp b/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp new file mode 100644 index 0000000000000..b1977d5713627 --- /dev/null +++ b/clang/test/CIR/CodeGenCXX/vtable-linkage.cpp @@ -0,0 +1,258 @@ +// RUN: %clang_cc1 -triple x86_64-pc-linux -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir +// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR +// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -fclangir -emit-llvm -o - | FileCheck %s --check-prefixes=LLVM +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -emit-llvm -o - | FileCheck %s --check-prefixes=LLVM +// +// RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++03 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir +// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR +// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++03 -fclangir -emit-llvm -o - | FileCheck %s --check-prefixes=LLVM +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++03 -emit-llvm -o - | FileCheck %s --check-prefixes=LLVM +// +// RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++11 -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir +// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR +// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++11 -fclangir -emit-llvm -o - | FileCheck %s --check-prefixes=LLVM +// RUN: %clang_cc1 %s -triple=x86_64-pc-linux -std=c++11 -emit-llvm -o - | FileCheck %s --check-prefixes=LLVM + +namespace { + struct A { + virtual void f() { } + }; +} + +void f() { A b; } + +struct B { + B(); + virtual void f(); +}; + +B::B() { } + +struct C : virtual B { + C(); + virtual void f() { } +}; + +C::C() { } + +struct D { + virtual void f(); +}; + +void D::f() { } + +static struct : D { } e; + +// Force 'e' to be constructed and therefore have a vtable defined. +void use_e() { + e.f(); +} + +// The destructor is the key function. +template<typename T> +struct E { + virtual ~E(); +}; + +template<typename T> E<T>::~E() { } + +// Anchor is the key function +template<> +struct E<char> { + virtual void anchor(); +}; + +void E<char>::anchor() { } + +template struct E<short>; +extern template struct E<int>; + +void use_E() { + E<int> ei; + (void)ei; + E<long> el; + (void)el; +} + +// No key function +template<typename T> +struct F { + virtual void foo() { } +}; + +// No key function +template<> +struct F<char> { + virtual void foo() { } +}; + +template struct F<short>; +extern template struct F<int>; + +void use_F() { + F<char> fc; + fc.foo(); + F<int> fi; + fi.foo(); + F<long> fl; + (void)fl; +} + +// B has a key function that is not defined in this translation unit so its vtable +// has external linkage. +// CIR-DAG: cir.global "private" external @_ZTV1B : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1B = external {{.*}}{ [3 x ptr] }, align 8 + +// C has no key function, so its vtable should have weak_odr linkage +// and hidden visibility. +// CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1C = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1C> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1C1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 5>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global linkonce_odr comdat @_ZTS1C = #cir.const_array<"1C" : !cir.array<!s8i x 2>, trailing_zeros> : !cir.array<!s8i x 3> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1C = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv121__vmi_class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1C> : !cir.ptr<!u8i>, #cir.int<0> : !u32i, #cir.int<1> : !u32i, #cir.global_view<@_ZTI1B> : !cir.ptr<!u8i>, #cir.int<-8189> : !s64i}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global linkonce_odr comdat @_ZTT1C = #cir.const_array<[#cir.global_view<@_ZTV1C, [0 : i32, 4 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTV1C, [0 : i32, 4 : i32]> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 2> {alignment = 8 : i64} +// LLVM-DAG: @_ZTV1C = linkonce_odr {{.*}}{ [5 x ptr] } { [5 x ptr] [ptr null, ptr null, ptr null, ptr @_ZTI1C, ptr @_ZN1C1fEv] }, comdat, align 8 +// LLVM-DAG: @_ZTS1C = linkonce_odr {{.*}}[{{[0-9]}} x i8] c"1C\00", comdat, align 1 +// LLVM-DAG: @_ZTI1C = {{.*}}{ ptr, ptr, i32, i32, ptr, i64 } { ptr getelementptr{{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv121__vmi_class_type_infoE, i64 {{.*}}), ptr @_ZTS1C, i32 0, i32 1, ptr @_ZTI1B, i64 -8189 } +// LLVM-DAG: @_ZTT1C = linkonce_odr {{.*}}[2 x ptr] [ptr getelementptr inbounds{{.*}}({{.*}}, ptr @_ZTV1C, {{.*}}, ptr getelementptr inbounds {{.*}}({{.*}}, ptr @_ZTV1C{{.*}})] + +// D has a key function that is defined in this translation unit so its vtable is +// defined in the translation unit. +// CIR-DAG: cir.global "private" external @_ZTV1D = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1D> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1D1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global external @_ZTS1D = #cir.const_array<"1D" : !cir.array<!s8i x 2>, trailing_zeros> : !cir.array<!s8i x 3> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1D = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1D> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1D = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1D, ptr @_ZN1D1fEv] }, align 8 +// LLVM-DAG: @_ZTS1D = {{.*}}[{{[0-9]}} x i8] c"1D\00", align 1 +// LLVM-DAG: @_ZTI1D = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{[0-9]+}}), ptr @_ZTS1D }, align 8 + +// E<char> is an explicit specialization with a key function defined +// in this translation unit, so its vtable should have external +// linkage. +// CIR-DAG: cir.global "private" external @_ZTV1EIcE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1EIcE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1EIcE6anchorEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global external @_ZTS1EIcE = #cir.const_array<"1EIcE" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1EIcE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1EIcE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1EIcE = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1EIcE, ptr @_ZN1EIcE6anchorEv] }, align 8 +// LLVM-DAG: @_ZTS1EIcE = {{.*}}[6 x i8] c"1EIcE\00", align 1 +// LLVM-DAG: @_ZTI1EIcE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1EIcE }, align 8 + +// E<short> is an explicit template instantiation with a key function +// defined in this translation unit, so its vtable should have +// weak_odr linkage. +// CIR-DAG: cir.global "private" weak_odr comdat @_ZTV1EIsE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1EIsE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1EIsED1Ev> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1EIsED0Ev> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global constant external @_ZTI1EIsE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1EIsE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global weak_odr comdat @_ZTS1EIsE = #cir.const_array<"1EIsE" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64} +// LLVM-DAG: @_ZTV1EIsE = {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1EIsE, ptr @_ZN1EIsED1Ev, ptr @_ZN1EIsED0Ev] }, comdat, align 8 +// LLVM-DAG: @_ZTI1EIsE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1EIsE } +// LLVM-DAG: @_ZTS1EIsE = {{.*}}[6 x i8] c"1EIsE\00" + +// F<short> is an explicit template instantiation without a key +// function, so its vtable should have weak_odr linkage +// CIR-DAG: cir.global "private" weak_odr comdat @_ZTV1FIsE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1FIsE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1FIsE3fooEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global weak_odr comdat @_ZTS1FIsE = #cir.const_array<"1FIsE" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1FIsE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1FIsE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1FIsE = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1FIsE, ptr @_ZN1FIsE3fooEv] }, comdat, align 8 +// LLVM-DAG: @_ZTS1FIsE = {{.*}}[6 x i8] c"1FIsE\00", comdat, align 1 +// LLVM-DAG: @_ZTI1FIsE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1FIsE } + +// E<long> is an implicit template instantiation with a key function +// defined in this translation unit, so its vtable should have +// linkonce_odr linkage. +// CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1EIlE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1EIlE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1EIlED1Ev> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1EIlED0Ev> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global linkonce_odr comdat @_ZTS1EIlE = #cir.const_array<"1EIlE" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1EIlE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1EIlE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1EIlE = {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1EIlE, ptr @_ZN1EIlED1Ev, ptr @_ZN1EIlED0Ev] }, comdat, align 8 +// LLVM-DAG: @_ZTS1EIlE = {{.*}}[6 x i8] c"1EIlE\00", comdat, align 1 +// LLVM-DAG: @_ZTI1EIlE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1EIlE } + +// F<long> is an implicit template instantiation with no key function, +// so its vtable should have linkonce_odr linkage. +// CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1FIlE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1FIlE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1FIlE3fooEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global linkonce_odr comdat @_ZTS1FIlE = #cir.const_array<"1FIlE" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1FIlE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1FIlE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1FIlE = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1FIlE, ptr @_ZN1FIlE3fooEv] }, comdat, align 8 +// LLVM-DAG: @_ZTS1FIlE = {{.*}}[6 x i8] c"1FIlE\00", comdat, align 1 +// LLVM-DAG: @_ZTI1FIlE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1FIlE } + +// F<int> is an explicit template instantiation declaration without a +// key function, so its vtable should have external linkage. +// CIR-DAG: cir.global "private" external @_ZTV1FIiE : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1FIiE = {{.*}}{ [3 x ptr] }, align 8 + +// E<int> is an explicit template instantiation declaration. It has a +// key function is not instantiated, so we know that vtable definition +// will be generated in TU where key function will be defined +// so we can mark it as external (without optimizations) and +// available_externally (with optimizations) because all of the inline +// virtual functions have been emitted. +// CIR-DAG: cir.global "private" external @_ZTV1EIiE : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1EIiE = {{.*}}{ [4 x ptr] }, align 8 + +// The anonymous struct for e has no linkage, so the vtable should have +// internal linkage. +// CIR-DAG: cir.global "private" internal dso_local @_ZTV3$_0 = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI3$_0> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1D1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global internal dso_local @_ZTS3$_0 = #cir.const_array<"3$_0" : !cir.array<!s8i x 4>, trailing_zeros> : !cir.array<!s8i x 5> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI3$_0 = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv120__si_class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS3$_0> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1D> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @"_ZTV3$_0" = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @"_ZTI3$_0", ptr @_ZN1D1fEv] }, align 8 +// LLVM-DAG: @"_ZTS3$_0" = {{.*}}[5 x i8] c"3$_0\00", align 1 +// LLVM-DAG: @"_ZTI3$_0" = {{.*}}{ ptr, ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv120__si_class_type_infoE, i64 {{.*}}), ptr @"_ZTS3$_0", ptr @_ZTI1D }, align 8 + +// The A vtable should have internal linkage since it is inside an anonymous +// namespace. +// CIR-DAG: cir.global "private" internal dso_local @_ZTVN12_GLOBAL__N_11AE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTIN12_GLOBAL__N_11AE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN12_GLOBAL__N_11A1fEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global internal dso_local @_ZTSN12_GLOBAL__N_11AE = #cir.const_array<"N12_GLOBAL__N_11AE" : !cir.array<!s8i x 18>, trailing_zeros> : !cir.array<!s8i x 19> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTIN12_GLOBAL__N_11AE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTSN12_GLOBAL__N_11AE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTVN12_GLOBAL__N_11AE = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTIN12_GLOBAL__N_11AE, ptr @_ZN12_GLOBAL__N_11A1fEv] }, align 8 +// LLVM-DAG: @_ZTSN12_GLOBAL__N_11AE = {{.*}}[19 x i8] c"N12_GLOBAL__N_11AE\00", align 1 +// LLVM-DAG: @_ZTIN12_GLOBAL__N_11AE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTSN12_GLOBAL__N_11AE }, align 8 + +// F<char> is an explicit specialization without a key function, so +// its vtable should have linkonce_odr linkage. +// CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1FIcE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1FIcE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1FIcE3fooEv> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 3>}> : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global linkonce_odr comdat @_ZTS1FIcE = #cir.const_array<"1FIcE" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6> {alignment = 1 : i64} +// CIR-DAG: cir.global constant external @_ZTI1FIcE = #cir.typeinfo<{#cir.global_view<@_ZTVN10__cxxabiv117__class_type_infoE, [2 : i32]> : !cir.ptr<!u8i>, #cir.global_view<@_ZTS1FIcE> : !cir.ptr<!u8i>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1FIcE = {{.*}}{ [3 x ptr] } { [3 x ptr] [ptr null, ptr @_ZTI1FIcE, ptr @_ZN1FIcE3fooEv] }, comdat, align 8 +// LLVM-DAG: @_ZTS1FIcE = {{.*}}[6 x i8] c"1FIcE\00", comdat, align 1 +// LLVM-DAG: @_ZTI1FIcE = {{.*}}{ ptr, ptr } { ptr getelementptr {{.*}}({{.*}}, ptr @_ZTVN10__cxxabiv117__class_type_infoE, i64 {{.*}}), ptr @_ZTS1FIcE } + +// CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1GIiE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1GIiE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1GIiE2f0Ev> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1GIiE2f1Ev> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1GIiE = {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1GIiE, ptr @_ZN1GIiE2f0Ev, ptr @_ZN1GIiE2f1Ev] }, comdat, align 8 +template <typename T> +class G { +public: + G() {} + virtual void f0(); + virtual void f1(); +}; +template <> +void G<int>::f1() {} +template <typename T> +void G<T>::f0() {} +void G_f0() { new G<int>(); } + +// H<int> has a key function without a body but it's a template instantiation +// so its VTable must be emitted. +// CIR-DAG: cir.global "private" linkonce_odr comdat @_ZTV1HIiE = #cir.vtable<{#cir.const_array<[#cir.ptr<null> : !cir.ptr<!u8i>, #cir.global_view<@_ZTI1HIiE> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1HIiED1Ev> : !cir.ptr<!u8i>, #cir.global_view<@_ZN1HIiED0Ev> : !cir.ptr<!u8i>]> : !cir.array<!cir.ptr<!u8i> x 4>}> : !{{.*}}{alignment = 8 : i64} +// LLVM-DAG: @_ZTV1HIiE = {{.*}}{ [4 x ptr] } { [4 x ptr] [ptr null, ptr @_ZTI1HIiE, ptr @_ZN1HIiED1Ev, ptr @_ZN1HIiED0Ev] }, comdat, align 8 +template <typename T> +class H { +public: + virtual ~H(); +}; + +void use_H() { + H<int> h; +} + +// I<int> has an explicit instantiation declaration and needs a VTT and +// construction vtables. + +// CIR-DAG: cir.global "private" external @_ZTV1IIiE : !{{.*}}{alignment = 8 : i64} +// CIR-DAG: cir.global "private" external @_ZTT1IIiE : !cir.array<!cir.ptr<!u8i> x 4> {alignment = 8 : i64} +// LLVM-DAG: @_ZTV1IIiE = {{.*}}{ [5 x ptr] }, align 8 +// LLVM-DAG: @_ZTT1IIiE = {{.*}}[4 x ptr], align 8 +struct VBase1 { virtual void f(); }; struct VBase2 : virtual VBase1 {}; +template<typename T> +struct I : VBase2 {}; +extern template struct I<int>; +I<int> i; diff --git a/clang/test/CIR/CodeGenCXX/vtable-virt-thunk-adj.cpp b/clang/test/CIR/CodeGenCXX/vtable-virt-thunk-adj.cpp new file mode 100644 index 0000000000000..e8f955c1d1cc9 --- /dev/null +++ b/clang/test/CIR/CodeGenCXX/vtable-virt-thunk-adj.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare -o %t.cir 2> %t-before.cir +// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR +// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR +// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -fclangir -emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=LLVM +// RUN: %clang_cc1 -I%S/Inputs %s -triple x86_64-apple-darwin10 -emit-llvm -std=c++11 -o - | FileCheck %s --check-prefixes=LLVM + +#include <typeinfo> + +struct Item { + const std::type_info &ti; + const char *name; + void *(*make)(); +}; + +template<typename T> void *make_impl() { return new T; } +template<typename T> constexpr Item item(const char *name) { + return { typeid(T), name, make_impl<T> }; +} + + +struct A { virtual ~A(); }; +struct B { virtual ~B(); }; +struct C : virtual A, virtual B {}; + +extern constexpr Item items[] = { + item<B>("B"), item<C>("C") +}; + +// CIR-LABEL: cir.func {{.*}}@_ZTv0_n24_N1CD1Ev +// CIR: %[[THIS:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["this", init] +// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS]] : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C> +// CIR: %[[THIS_CAST:.*]] = cir.cast bitcast %[[THIS_LOAD]] : !cir.ptr<!rec_C> -> !cir.ptr<!u8i> +// CIR: %[[VTBL_PTR:.*]] = cir.vtable.get_vptr %[[THIS_CAST]] : !cir.ptr<!u8i> -> !cir.ptr<!cir.vptr> +// CIR: %[[VTBL_LOAD:.*]] = cir.load {{.*}}%[[VTBL_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr +// CIR: %[[VTBL_CAST:.*]] = cir.cast bitcast %[[VTBL_LOAD]] : !cir.vptr -> !cir.ptr<!u8i> +// CIR: %[[OFFSET:.*]] = cir.const #cir.int<-24> : !s64i +// CIR: %[[OFFSET_STRIDE:.*]] = cir.ptr_stride %[[VTBL_CAST]], %[[OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> +// CIR: %[[OFFSET_CAST:.*]] = cir.cast bitcast %[[OFFSET_STRIDE]] : !cir.ptr<!u8i> -> !cir.ptr<!s64i> +// CIR: %[[OFFSET_LOAD:.*]] = cir.load align(8) %[[OFFSET_CAST]] : !cir.ptr<!s64i>, !s64i +// CIR: %[[APPLY_OFFSET:.*]] = cir.ptr_stride %[[THIS_CAST]], %[[OFFSET_LOAD]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> +// CIR: %[[CAST_THIS_BACK:.*]] = cir.cast bitcast %[[APPLY_OFFSET]] : !cir.ptr<!u8i> -> !cir.ptr<!rec_C> +// CIR: cir.call @_ZN1CD1Ev(%[[CAST_THIS_BACK]]) +// +// LLVM-LABEL: define{{.*}}@_ZTv0_n24_N1CD1Ev +// LLVM: %[[THIS:.*]] = alloca ptr +// LLVM: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS]] +// LLVM: %[[VTBL_LOAD:.*]] = load ptr, ptr %[[THIS_LOAD]] +// LLVM: %[[OFFSET_STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[VTBL_LOAD]], i64 -24 +// LLVM: %[[OFFSET_LOAD:.*]] = load i64, ptr %[[OFFSET_STRIDE]] +// LLVM: %[[APPLY_OFFSET:.*]] = getelementptr {{.*}}i8, ptr %[[THIS_LOAD]], i64 %[[OFFSET_LOAD]] +// LLVM: call void @_ZN1CD1Ev(ptr {{.*}}%[[APPLY_OFFSET]]) +// +// CIR-LABEL: cir.func {{.*}}@_ZTv0_n24_N1CD0Ev +// CIR: %[[THIS:.*]] = cir.alloca !cir.ptr<!rec_C>, !cir.ptr<!cir.ptr<!rec_C>>, ["this", init] +// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS]] : !cir.ptr<!cir.ptr<!rec_C>>, !cir.ptr<!rec_C> +// CIR: %[[THIS_CAST:.*]] = cir.cast bitcast %[[THIS_LOAD]] : !cir.ptr<!rec_C> -> !cir.ptr<!u8i> +// CIR: %[[VTBL_PTR:.*]] = cir.vtable.get_vptr %[[THIS_CAST]] : !cir.ptr<!u8i> -> !cir.ptr<!cir.vptr> +// CIR: %[[VTBL_LOAD:.*]] = cir.load {{.*}}%[[VTBL_PTR]] : !cir.ptr<!cir.vptr>, !cir.vptr +// CIR: %[[VTBL_CAST:.*]] = cir.cast bitcast %[[VTBL_LOAD]] : !cir.vptr -> !cir.ptr<!u8i> +// CIR: %[[OFFSET:.*]] = cir.const #cir.int<-24> : !s64i +// CIR: %[[OFFSET_STRIDE:.*]] = cir.ptr_stride %[[VTBL_CAST]], %[[OFFSET]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> +// CIR: %[[OFFSET_CAST:.*]] = cir.cast bitcast %[[OFFSET_STRIDE]] : !cir.ptr<!u8i> -> !cir.ptr<!s64i> +// CIR: %[[OFFSET_LOAD:.*]] = cir.load align(8) %[[OFFSET_CAST]] : !cir.ptr<!s64i>, !s64i +// CIR: %[[APPLY_OFFSET:.*]] = cir.ptr_stride %[[THIS_CAST]], %[[OFFSET_LOAD]] : (!cir.ptr<!u8i>, !s64i) -> !cir.ptr<!u8i> +// CIR: %[[CAST_THIS_BACK:.*]] = cir.cast bitcast %[[APPLY_OFFSET]] : !cir.ptr<!u8i> -> !cir.ptr<!rec_C> +// CIR: cir.call @_ZN1CD0Ev(%[[CAST_THIS_BACK]]) +// +// LLVM-LABEL: define{{.*}}@_ZTv0_n24_N1CD0Ev +// LLVM: %[[THIS:.*]] = alloca ptr +// LLVM: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS]] +// LLVM: %[[VTBL_LOAD:.*]] = load ptr, ptr %[[THIS_LOAD]] +// LLVM: %[[OFFSET_STRIDE:.*]] = getelementptr {{.*}}i8, ptr %[[VTBL_LOAD]], i64 -24 +// LLVM: %[[OFFSET_LOAD:.*]] = load i64, ptr %[[OFFSET_STRIDE]] +// LLVM: %[[APPLY_OFFSET:.*]] = getelementptr {{.*}}i8, ptr %[[THIS_LOAD]], i64 %[[OFFSET_LOAD]] +// LLVM: call void @_ZN1CD0Ev(ptr {{.*}}%[[APPLY_OFFSET]]) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
