@@ -105,6 +105,10 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
105105 const CXXDestructorDecl *dtor,
106106 CXXDtorType dtorType, Address thisAddr,
107107 DeleteOrMemberCallExpr e) override ;
108+
109+ bool canSpeculativelyEmitVTable (const CXXRecordDecl *RD) const override ;
110+ bool canSpeculativelyEmitVTableAsBaseClass (const CXXRecordDecl *RD) const ;
111+
108112 mlir::Value getVTableAddressPoint (BaseSubobject base,
109113 const CXXRecordDecl *vtableClass) override ;
110114 mlir::Value getVTableAddressPointInStructorWithVTT (
@@ -221,6 +225,10 @@ class CIRGenItaniumCXXABI : public CIRGenCXXABI {
221225 // / the current ABI.
222226 RTTIUniquenessKind
223227 classifyRTTIUniqueness (QualType canTy, cir::GlobalLinkageKind linkage) const ;
228+
229+ private:
230+ bool hasAnyUnusedVirtualInlineFunction (const CXXRecordDecl *rd) const ;
231+ bool isVTableHidden (const CXXRecordDecl *rd) const ;
224232};
225233
226234} // namespace
@@ -1789,7 +1797,7 @@ cir::GlobalOp CIRGenItaniumCXXABI::getAddrOfVTable(const CXXRecordDecl *rd,
17891797 return vtable;
17901798
17911799 // Queue up this vtable for possible deferred emission.
1792- assert (! cir::MissingFeatures::deferredVtables () );
1800+ cgm. addDeferredVTable (rd );
17931801
17941802 SmallString<256 > name;
17951803 llvm::raw_svector_ostream out (name);
@@ -2575,6 +2583,126 @@ void CIRGenItaniumCXXABI::emitBeginCatch(CIRGenFunction &cgf,
25752583 cgf.emitAutoVarCleanups (var);
25762584}
25772585
2586+ bool CIRGenItaniumCXXABI::hasAnyUnusedVirtualInlineFunction (
2587+ const CXXRecordDecl *rd) const {
2588+ const auto &vtableLayout = cgm.getItaniumVTableContext ().getVTableLayout (rd);
2589+
2590+ for (const auto &vtableComponent : vtableLayout.vtable_components ()) {
2591+ // Skip empty slot.
2592+ if (!vtableComponent.isUsedFunctionPointerKind ())
2593+ continue ;
2594+
2595+ const CXXMethodDecl *method = vtableComponent.getFunctionDecl ();
2596+ const FunctionDecl *fd = method->getDefinition ();
2597+ const bool isInlined =
2598+ method->getCanonicalDecl ()->isInlined () || (fd && fd->isInlined ());
2599+ if (!isInlined)
2600+ continue ;
2601+
2602+ StringRef name = cgm.getMangledName (
2603+ vtableComponent.getGlobalDecl (/* HasVectorDeletingDtors=*/ false ));
2604+ auto entry = dyn_cast_or_null<cir::GlobalOp>(cgm.getGlobalValue (name));
2605+ // This checks if virtual inline function has already been emitted.
2606+ // Note that it is possible that this inline function would be emitted
2607+ // after trying to emit vtable speculatively. Because of this we do
2608+ // an extra pass after emitting all deferred vtables to find and emit
2609+ // these vtables opportunistically.
2610+ if (!entry || entry.isDeclaration ())
2611+ return true ;
2612+ }
2613+ return false ;
2614+ }
2615+
2616+ bool CIRGenItaniumCXXABI::isVTableHidden (const CXXRecordDecl *rd) const {
2617+ const auto &vtableLayout = cgm.getItaniumVTableContext ().getVTableLayout (rd);
2618+
2619+ for (const auto &vtableComponent : vtableLayout.vtable_components ()) {
2620+ if (vtableComponent.isRTTIKind ()) {
2621+ const CXXRecordDecl *rttiDecl = vtableComponent.getRTTIDecl ();
2622+ if (rttiDecl->getVisibility () == Visibility::HiddenVisibility)
2623+ return true ;
2624+ } else if (vtableComponent.isUsedFunctionPointerKind ()) {
2625+ const CXXMethodDecl *method = vtableComponent.getFunctionDecl ();
2626+ if (method->getVisibility () == Visibility::HiddenVisibility &&
2627+ !method->isDefined ())
2628+ return true ;
2629+ }
2630+ }
2631+ return false ;
2632+ }
2633+
2634+ bool CIRGenItaniumCXXABI::canSpeculativelyEmitVTableAsBaseClass (
2635+ const CXXRecordDecl *rd) const {
2636+ // We don't emit available_externally vtables if we are in -fapple-kext mode
2637+ // because kext mode does not permit devirtualization.
2638+ if (cgm.getLangOpts ().AppleKext )
2639+ return false ;
2640+
2641+ // If the vtable is hidden then it is not safe to emit an available_externally
2642+ // copy of vtable.
2643+ if (isVTableHidden (rd))
2644+ return false ;
2645+
2646+ if (cgm.getCodeGenOpts ().ForceEmitVTables )
2647+ return true ;
2648+
2649+ // A speculative vtable can only be generated if all virtual inline functions
2650+ // defined by this class are emitted. The vtable in the final program contains
2651+ // for each virtual inline function not used in the current TU a function that
2652+ // is equivalent to the unused function. The function in the actual vtable
2653+ // does not have to be declared under the same symbol (e.g., a virtual
2654+ // destructor that can be substituted with its base class's destructor). Since
2655+ // inline functions are emitted lazily and this emissions does not account for
2656+ // speculative emission of a vtable, we might generate a speculative vtable
2657+ // with references to inline functions that are not emitted under that name.
2658+ // This can lead to problems when devirtualizing a call to such a function,
2659+ // that result in linking errors. Hence, if there are any unused virtual
2660+ // inline function, we cannot emit the speculative vtable.
2661+ // FIXME we can still emit a copy of the vtable if we
2662+ // can emit definition of the inline functions.
2663+ if (hasAnyUnusedVirtualInlineFunction (rd))
2664+ return false ;
2665+
2666+ // For a class with virtual bases, we must also be able to speculatively
2667+ // emit the VTT, because CodeGen doesn't have separate notions of "can emit
2668+ // the vtable" and "can emit the VTT". For a base subobject, this means we
2669+ // need to be able to emit non-virtual base vtables.
2670+ if (rd->getNumVBases ()) {
2671+ for (const auto &b : rd->bases ()) {
2672+ auto *brd = b.getType ()->getAsCXXRecordDecl ();
2673+ assert (brd && " no class for base specifier" );
2674+ if (b.isVirtual () || !brd->isDynamicClass ())
2675+ continue ;
2676+ if (!canSpeculativelyEmitVTableAsBaseClass (brd))
2677+ return false ;
2678+ }
2679+ }
2680+
2681+ return true ;
2682+ }
2683+
2684+ bool CIRGenItaniumCXXABI::canSpeculativelyEmitVTable (
2685+ const CXXRecordDecl *rd) const {
2686+ if (!canSpeculativelyEmitVTableAsBaseClass (rd))
2687+ return false ;
2688+
2689+ if (rd->shouldEmitInExternalSource ())
2690+ return false ;
2691+
2692+ // For a complete-object vtable (or more specifically, for the VTT), we need
2693+ // to be able to speculatively emit the vtables of all dynamic virtual bases.
2694+ for (const auto &b : rd->vbases ()) {
2695+ auto *brd = b.getType ()->getAsCXXRecordDecl ();
2696+ assert (brd && " no class for base specifier" );
2697+ if (!brd->isDynamicClass ())
2698+ continue ;
2699+ if (!canSpeculativelyEmitVTableAsBaseClass (brd))
2700+ return false ;
2701+ }
2702+
2703+ return true ;
2704+ }
2705+
25782706static mlir::Value performTypeAdjustment (CIRGenFunction &cgf,
25792707 Address initialPtr,
25802708 const CXXRecordDecl *unadjustedClass,
@@ -2598,8 +2726,23 @@ static mlir::Value performTypeAdjustment(CIRGenFunction &cgf,
25982726 // Perform the virtual adjustment if we have one.
25992727 mlir::Value resultPtr;
26002728 if (virtualAdjustment) {
2601- cgf.cgm .errorNYI (" virtual adjustment in thunk" );
2602- resultPtr = v;
2729+ mlir::Value vtablePtr = cgf.getVTablePtr (
2730+ loc, Address (v, clang::CharUnits::One ()), unadjustedClass);
2731+ vtablePtr = builder.createBitcast (vtablePtr, i8PtrTy);
2732+
2733+ mlir::Value offset;
2734+ mlir::Value offsetPtr =
2735+ cir::PtrStrideOp::create (builder, loc, i8PtrTy, vtablePtr,
2736+ builder.getSInt64 (virtualAdjustment, loc));
2737+ if (cgf.cgm .getItaniumVTableContext ().isRelativeLayout ()) {
2738+ assert (!cir::MissingFeatures::vtableRelativeLayout ());
2739+ cgf.cgm .errorNYI (" virtual adjustment for relative layout vtables" );
2740+ } else {
2741+ offset = builder.createAlignedLoad (loc, cgf.ptrDiffTy , offsetPtr,
2742+ cgf.getPointerAlign ());
2743+ }
2744+
2745+ resultPtr = cir::PtrStrideOp::create (builder, loc, i8PtrTy, v, offset);
26032746 } else {
26042747 resultPtr = v;
26052748 }
0 commit comments