diff options
Diffstat (limited to 'llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp')
| -rw-r--r-- | llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp | 107 |
1 files changed, 85 insertions, 22 deletions
diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp index cf1ff405c493..7a8946110785 100644 --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -94,6 +94,7 @@ #include "llvm/Support/MathExtras.h" #include "llvm/Transforms/IPO.h" #include "llvm/Transforms/IPO/FunctionAttrs.h" +#include "llvm/Transforms/Utils/BasicBlockUtils.h" #include "llvm/Transforms/Utils/Evaluator.h" #include <algorithm> #include <cstddef> @@ -144,24 +145,32 @@ static cl::opt<bool> /// This is needed to support legacy tests that don't contain /// !vcall_visibility metadata (the mere presense of type tests /// previously implied hidden visibility). -cl::opt<bool> +static cl::opt<bool> WholeProgramVisibility("whole-program-visibility", cl::init(false), cl::Hidden, cl::ZeroOrMore, cl::desc("Enable whole program visibility")); /// Provide a way to force disable whole program for debugging or workarounds, /// when enabled via the linker. -cl::opt<bool> DisableWholeProgramVisibility( +static cl::opt<bool> DisableWholeProgramVisibility( "disable-whole-program-visibility", cl::init(false), cl::Hidden, cl::ZeroOrMore, cl::desc("Disable whole program visibility (overrides enabling options)")); /// Provide way to prevent certain function from being devirtualized -cl::list<std::string> +static cl::list<std::string> SkipFunctionNames("wholeprogramdevirt-skip", cl::desc("Prevent function(s) from being devirtualized"), cl::Hidden, cl::ZeroOrMore, cl::CommaSeparated); +/// Mechanism to add runtime checking of devirtualization decisions, trapping on +/// any that are not correct. Useful for debugging undefined behavior leading to +/// failures with WPD. +static cl::opt<bool> + CheckDevirt("wholeprogramdevirt-check", cl::init(false), cl::Hidden, + cl::ZeroOrMore, + cl::desc("Add code to trap on incorrect devirtualizations")); + namespace { struct PatternList { std::vector<GlobPattern> Patterns; @@ -509,6 +518,11 @@ struct DevirtModule { MapVector<VTableSlot, VTableSlotInfo> CallSlots; + // Calls that have already been optimized. We may add a call to multiple + // VTableSlotInfos if vtable loads are coalesced and need to make sure not to + // optimize a call more than once. + SmallPtrSet<CallBase *, 8> OptimizedCalls; + // This map keeps track of the number of "unsafe" uses of a loaded function // pointer. The key is the associated llvm.type.test intrinsic call generated // by this pass. An unsafe use is one that calls the loaded function pointer @@ -777,8 +791,9 @@ namespace llvm { /// If whole program visibility asserted, then upgrade all public vcall /// visibility metadata on vtable definitions to linkage unit visibility in /// Module IR (for regular or hybrid LTO). -void updateVCallVisibilityInModule(Module &M, - bool WholeProgramVisibilityEnabledInLTO) { +void updateVCallVisibilityInModule( + Module &M, bool WholeProgramVisibilityEnabledInLTO, + const DenseSet<GlobalValue::GUID> &DynamicExportSymbols) { if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) return; for (GlobalVariable &GV : M.globals()) @@ -786,22 +801,29 @@ void updateVCallVisibilityInModule(Module &M, // the vtable definitions. We won't have an existing vcall_visibility // metadata on vtable definitions with public visibility. if (GV.hasMetadata(LLVMContext::MD_type) && - GV.getVCallVisibility() == GlobalObject::VCallVisibilityPublic) + GV.getVCallVisibility() == GlobalObject::VCallVisibilityPublic && + // Don't upgrade the visibility for symbols exported to the dynamic + // linker, as we have no information on their eventual use. + !DynamicExportSymbols.count(GV.getGUID())) GV.setVCallVisibilityMetadata(GlobalObject::VCallVisibilityLinkageUnit); } /// If whole program visibility asserted, then upgrade all public vcall /// visibility metadata on vtable definition summaries to linkage unit /// visibility in Module summary index (for ThinLTO). -void updateVCallVisibilityInIndex(ModuleSummaryIndex &Index, - bool WholeProgramVisibilityEnabledInLTO) { +void updateVCallVisibilityInIndex( + ModuleSummaryIndex &Index, bool WholeProgramVisibilityEnabledInLTO, + const DenseSet<GlobalValue::GUID> &DynamicExportSymbols) { if (!hasWholeProgramVisibility(WholeProgramVisibilityEnabledInLTO)) return; for (auto &P : Index) { for (auto &S : P.second.SummaryList) { auto *GVar = dyn_cast<GlobalVarSummary>(S.get()); - if (!GVar || GVar->vTableFuncs().empty() || - GVar->getVCallVisibility() != GlobalObject::VCallVisibilityPublic) + if (!GVar || + GVar->getVCallVisibility() != GlobalObject::VCallVisibilityPublic || + // Don't upgrade the visibility for symbols exported to the dynamic + // linker, as we have no information on their eventual use. + DynamicExportSymbols.count(P.first)) continue; GVar->setVCallVisibility(GlobalObject::VCallVisibilityLinkageUnit); } @@ -901,7 +923,7 @@ bool DevirtModule::runForTesting( ExitOnErr(errorCodeToError(EC)); WriteIndexToFile(*Summary, OS); } else { - raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_Text); + raw_fd_ostream OS(ClWriteSummary, EC, sys::fs::OF_TextWithCRLF); ExitOnErr(errorCodeToError(EC)); yaml::Output Out(OS); Out << *Summary; @@ -986,12 +1008,10 @@ bool DevirtIndex::tryFindVirtualCallTargets( std::vector<ValueInfo> &TargetsForSlot, const TypeIdCompatibleVtableInfo TIdInfo, uint64_t ByteOffset) { for (const TypeIdOffsetVtableInfo &P : TIdInfo) { - // Find the first non-available_externally linkage vtable initializer. + // Find a representative copy of the vtable initializer. // We can have multiple available_externally, linkonce_odr and weak_odr - // vtable initializers, however we want to skip available_externally as they - // do not have type metadata attached, and therefore the summary will not - // contain any vtable functions. We can also have multiple external - // vtable initializers in the case of comdats, which we cannot check here. + // vtable initializers. We can also have multiple external vtable + // initializers in the case of comdats, which we cannot check here. // The linker should give an error in this case. // // Also, handle the case of same-named local Vtables with the same path @@ -1006,14 +1026,27 @@ bool DevirtIndex::tryFindVirtualCallTargets( return false; LocalFound = true; } - if (!GlobalValue::isAvailableExternallyLinkage(S->linkage())) { - VS = cast<GlobalVarSummary>(S->getBaseObject()); + auto *CurVS = cast<GlobalVarSummary>(S->getBaseObject()); + if (!CurVS->vTableFuncs().empty() || + // Previously clang did not attach the necessary type metadata to + // available_externally vtables, in which case there would not + // be any vtable functions listed in the summary and we need + // to treat this case conservatively (in case the bitcode is old). + // However, we will also not have any vtable functions in the + // case of a pure virtual base class. In that case we do want + // to set VS to avoid treating it conservatively. + !GlobalValue::isAvailableExternallyLinkage(S->linkage())) { + VS = CurVS; // We cannot perform whole program devirtualization analysis on a vtable // with public LTO visibility. if (VS->getVCallVisibility() == GlobalObject::VCallVisibilityPublic) return false; } } + // There will be no VS if all copies are available_externally having no + // type metadata. In that case we can't safely perform WPD. + if (!VS) + return false; if (!VS->isLive()) continue; for (auto VTP : VS->vTableFuncs()) { @@ -1036,11 +1069,34 @@ void DevirtModule::applySingleImplDevirt(VTableSlotInfo &SlotInfo, return; auto Apply = [&](CallSiteInfo &CSInfo) { for (auto &&VCallSite : CSInfo.CallSites) { + if (!OptimizedCalls.insert(&VCallSite.CB).second) + continue; + if (RemarksEnabled) VCallSite.emitRemark("single-impl", TheFn->stripPointerCasts()->getName(), OREGetter); - VCallSite.CB.setCalledOperand(ConstantExpr::getBitCast( - TheFn, VCallSite.CB.getCalledOperand()->getType())); + auto &CB = VCallSite.CB; + assert(!CB.getCalledFunction() && "devirtualizing direct call?"); + IRBuilder<> Builder(&CB); + Value *Callee = + Builder.CreateBitCast(TheFn, CB.getCalledOperand()->getType()); + + // If checking is enabled, add support to compare the virtual function + // pointer to the devirtualized target. In case of a mismatch, perform a + // debug trap. + if (CheckDevirt) { + auto *Cond = Builder.CreateICmpNE(CB.getCalledOperand(), Callee); + Instruction *ThenTerm = + SplitBlockAndInsertIfThen(Cond, &CB, /*Unreachable=*/false); + Builder.SetInsertPoint(ThenTerm); + Function *TrapFn = Intrinsic::getDeclaration(&M, Intrinsic::debugtrap); + auto *CallTrap = Builder.CreateCall(TrapFn); + CallTrap->setDebugLoc(CB.getDebugLoc()); + } + + // Devirtualize. + CB.setCalledOperand(Callee); + // This use is no longer unsafe. if (VCallSite.NumUnsafeUses) --*VCallSite.NumUnsafeUses; @@ -1106,7 +1162,7 @@ bool DevirtModule::trySingleImplDevirt( // to make it visible to thin LTO objects. We can only get here during the // ThinLTO export phase. if (TheFn->hasLocalLinkage()) { - std::string NewName = (TheFn->getName() + "$merged").str(); + std::string NewName = (TheFn->getName() + ".llvm.merged").str(); // Since we are renaming the function, any comdats with the same name must // also be renamed. This is required when targeting COFF, as the comdat name @@ -1359,10 +1415,13 @@ bool DevirtModule::tryEvaluateFunctionsWithArgs( void DevirtModule::applyUniformRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, uint64_t TheRetVal) { - for (auto Call : CSInfo.CallSites) + for (auto Call : CSInfo.CallSites) { + if (!OptimizedCalls.insert(&Call.CB).second) + continue; Call.replaceAndErase( "uniform-ret-val", FnName, RemarksEnabled, OREGetter, ConstantInt::get(cast<IntegerType>(Call.CB.getType()), TheRetVal)); + } CSInfo.markDevirt(); } @@ -1468,6 +1527,8 @@ void DevirtModule::applyUniqueRetValOpt(CallSiteInfo &CSInfo, StringRef FnName, bool IsOne, Constant *UniqueMemberAddr) { for (auto &&Call : CSInfo.CallSites) { + if (!OptimizedCalls.insert(&Call.CB).second) + continue; IRBuilder<> B(&Call.CB); Value *Cmp = B.CreateICmp(IsOne ? ICmpInst::ICMP_EQ : ICmpInst::ICMP_NE, Call.VTable, @@ -1536,6 +1597,8 @@ bool DevirtModule::tryUniqueRetValOpt( void DevirtModule::applyVirtualConstProp(CallSiteInfo &CSInfo, StringRef FnName, Constant *Byte, Constant *Bit) { for (auto Call : CSInfo.CallSites) { + if (!OptimizedCalls.insert(&Call.CB).second) + continue; auto *RetType = cast<IntegerType>(Call.CB.getType()); IRBuilder<> B(&Call.CB); Value *Addr = |
