diff options
Diffstat (limited to 'llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp')
| -rw-r--r-- | llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp | 104 |
1 files changed, 100 insertions, 4 deletions
diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp index 61054e7ae46f..6acace1d9fd4 100644 --- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp +++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp @@ -359,6 +359,36 @@ template <> struct DenseMapInfo<VTableSlotSummary> { namespace { +// Returns true if the function must be unreachable based on ValueInfo. +// +// In particular, identifies a function as unreachable in the following +// conditions +// 1) All summaries are live. +// 2) All function summaries indicate it's unreachable +bool mustBeUnreachableFunction(ValueInfo TheFnVI) { + if ((!TheFnVI) || TheFnVI.getSummaryList().empty()) { + // Returns false if ValueInfo is absent, or the summary list is empty + // (e.g., function declarations). + return false; + } + + for (auto &Summary : TheFnVI.getSummaryList()) { + // Conservatively returns false if any non-live functions are seen. + // In general either all summaries should be live or all should be dead. + if (!Summary->isLive()) + return false; + if (auto *FS = dyn_cast<FunctionSummary>(Summary.get())) { + if (!FS->fflags().MustBeUnreachable) + return false; + } + // Do nothing if a non-function has the same GUID (which is rare). + // This is correct since non-function summaries are not relevant. + } + // All function summaries are live and all of them agree that the function is + // unreachble. + return true; +} + // A virtual call site. VTable is the loaded virtual table pointer, and CS is // the indirect virtual call. struct VirtualCallSite { @@ -562,10 +592,12 @@ struct DevirtModule { void buildTypeIdentifierMap( std::vector<VTableBits> &Bits, DenseMap<Metadata *, std::set<TypeMemberInfo>> &TypeIdMap); + bool tryFindVirtualCallTargets(std::vector<VirtualCallTarget> &TargetsForSlot, const std::set<TypeMemberInfo> &TypeMemberInfos, - uint64_t ByteOffset); + uint64_t ByteOffset, + ModuleSummaryIndex *ExportSummary); void applySingleImplDevirt(VTableSlotInfo &SlotInfo, Constant *TheFn, bool &IsExported); @@ -640,6 +672,23 @@ struct DevirtModule { bool run(); + // Look up the corresponding ValueInfo entry of `TheFn` in `ExportSummary`. + // + // Caller guarantees that `ExportSummary` is not nullptr. + static ValueInfo lookUpFunctionValueInfo(Function *TheFn, + ModuleSummaryIndex *ExportSummary); + + // Returns true if the function definition must be unreachable. + // + // Note if this helper function returns true, `F` is guaranteed + // to be unreachable; if it returns false, `F` might still + // be unreachable but not covered by this helper function. + // + // Implementation-wise, if function definition is present, IR is analyzed; if + // not, look up function flags from ExportSummary as a fallback. + static bool mustBeUnreachableFunction(Function *const F, + ModuleSummaryIndex *ExportSummary); + // Lower the module using the action and summary passed as command line // arguments. For testing purposes only. static bool @@ -969,7 +1018,8 @@ void DevirtModule::buildTypeIdentifierMap( bool DevirtModule::tryFindVirtualCallTargets( std::vector<VirtualCallTarget> &TargetsForSlot, - const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset) { + const std::set<TypeMemberInfo> &TypeMemberInfos, uint64_t ByteOffset, + ModuleSummaryIndex *ExportSummary) { for (const TypeMemberInfo &TM : TypeMemberInfos) { if (!TM.Bits->GV->isConstant()) return false; @@ -997,6 +1047,11 @@ bool DevirtModule::tryFindVirtualCallTargets( if (Fn->getName() == "__cxa_pure_virtual") continue; + // We can disregard unreachable functions as possible call targets, as + // unreachable functions shouldn't be called. + if (mustBeUnreachableFunction(Fn, ExportSummary)) + continue; + TargetsForSlot.push_back({Fn, &TM}); } @@ -1053,6 +1108,9 @@ bool DevirtIndex::tryFindVirtualCallTargets( if (VTP.VTableOffset != P.AddressPointOffset + ByteOffset) continue; + if (mustBeUnreachableFunction(VTP.FuncVI)) + continue; + TargetsForSlot.push_back(VTP.FuncVI); } } @@ -1744,7 +1802,7 @@ void DevirtModule::rebuildGlobal(VTableBits &B) { GlobalVariable::PrivateLinkage, NewInit, "", B.GV); NewGV->setSection(B.GV->getSection()); NewGV->setComdat(B.GV->getComdat()); - NewGV->setAlignment(MaybeAlign(B.GV->getAlignment())); + NewGV->setAlignment(B.GV->getAlign()); // Copy the original vtable's metadata to the anonymous global, adjusting // offsets as required. @@ -2014,6 +2072,44 @@ void DevirtModule::removeRedundantTypeTests() { } } +ValueInfo +DevirtModule::lookUpFunctionValueInfo(Function *TheFn, + ModuleSummaryIndex *ExportSummary) { + assert((ExportSummary != nullptr) && + "Caller guarantees ExportSummary is not nullptr"); + + const auto TheFnGUID = TheFn->getGUID(); + const auto TheFnGUIDWithExportedName = GlobalValue::getGUID(TheFn->getName()); + // Look up ValueInfo with the GUID in the current linkage. + ValueInfo TheFnVI = ExportSummary->getValueInfo(TheFnGUID); + // If no entry is found and GUID is different from GUID computed using + // exported name, look up ValueInfo with the exported name unconditionally. + // This is a fallback. + // + // The reason to have a fallback: + // 1. LTO could enable global value internalization via + // `enable-lto-internalization`. + // 2. The GUID in ExportedSummary is computed using exported name. + if ((!TheFnVI) && (TheFnGUID != TheFnGUIDWithExportedName)) { + TheFnVI = ExportSummary->getValueInfo(TheFnGUIDWithExportedName); + } + return TheFnVI; +} + +bool DevirtModule::mustBeUnreachableFunction( + Function *const F, ModuleSummaryIndex *ExportSummary) { + // First, learn unreachability by analyzing function IR. + if (!F->isDeclaration()) { + // A function must be unreachable if its entry block ends with an + // 'unreachable'. + return isa<UnreachableInst>(F->getEntryBlock().getTerminator()); + } + // Learn unreachability from ExportSummary if ExportSummary is present. + return ExportSummary && + ::mustBeUnreachableFunction( + DevirtModule::lookUpFunctionValueInfo(F, ExportSummary)); +} + bool DevirtModule::run() { // If only some of the modules were split, we cannot correctly perform // this transformation. We already checked for the presense of type tests @@ -2137,7 +2233,7 @@ bool DevirtModule::run() { cast<MDString>(S.first.TypeID)->getString()) .WPDRes[S.first.ByteOffset]; if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos, - S.first.ByteOffset)) { + S.first.ByteOffset, ExportSummary)) { if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) { DidVirtualConstProp |= |
