aboutsummaryrefslogtreecommitdiff
path: root/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp')
-rw-r--r--llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp104
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 |=