[clang] [llvm] Support VFE in thinLTO (PR #69735)

Teresa Johnson via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 6 18:20:03 PST 2023


================
@@ -34,12 +40,223 @@ static cl::opt<bool>
     ClEnableVFE("enable-vfe", cl::Hidden, cl::init(true),
                 cl::desc("Enable virtual function elimination"));
 
+static cl::opt<std::string> ClReadSummary(
+    "globaldce-read-summary",
+    cl::desc("Read summary from given bitcode before running pass"),
+    cl::Hidden);
+
 STATISTIC(NumAliases  , "Number of global aliases removed");
 STATISTIC(NumFunctions, "Number of functions removed");
 STATISTIC(NumIFuncs,    "Number of indirect functions removed");
 STATISTIC(NumVariables, "Number of global variables removed");
 STATISTIC(NumVFuncs,    "Number of virtual functions removed");
 
+namespace llvm {
+
+// Returning a representative summary for the vtable, also set isSafe.
+static const GlobalVarSummary *
+getVTableFuncsForTId(const TypeIdOffsetVtableInfo &P, bool &isSafe) {
+  // Find a representative copy of the vtable initializer.
+  const GlobalVarSummary *VS = nullptr;
+  bool LocalFound = false;
+  for (auto &S : P.VTableVI.getSummaryList()) {
+    if (GlobalValue::isLocalLinkage(S->linkage())) {
+      if (LocalFound) {
+        isSafe = false;
+        return nullptr;
+      }
+      LocalFound = true;
+    }
+    auto *CurVS = cast<GlobalVarSummary>(S->getBaseObject());
+    // Ignore if vTableFuncs is empty and vtable is available_externally.
+    if (!CurVS->vTableFuncs().empty() ||
+        !GlobalValue::isAvailableExternallyLinkage(S->linkage())) {
+      VS = CurVS;
+      if (VS->getVCallVisibility() == GlobalObject::VCallVisibilityPublic) {
+        isSafe = false;
+        return VS;
+      }
+    }
+  }
+
+  if (!VS) {
+    isSafe = false;
+    return nullptr;
+  }
+  if (!VS->isLive()) {
+    isSafe = true;
+    return nullptr;
+  }
+  isSafe = true;
+  return VS;
+}
+
+static void collectSafeVTables(
+    ModuleSummaryIndex &Summary,
+    DenseMap<GlobalValue::GUID, std::vector<StringRef>> &NameByGUID,
+    std::map<ValueInfo, std::vector<VirtFuncOffset>> &VFESafeVTablesAndFns) {
+  // Update VFESafeVTablesAndFns with information from summary.
+  for (auto &P : Summary.typeIdCompatibleVtableMap()) {
+    NameByGUID[GlobalValue::getGUID(P.first)].push_back(P.first);
+    LLVM_DEBUG(dbgs() << "TId " << GlobalValue::getGUID(P.first) << " "
+                      << P.first << "\n");
+  }
+  llvm::errs() << "VFEThinLTO number of TIds: " << NameByGUID.size() << "\n";
+
+  // VFESafeVTablesAndFns: map from VI for vTable to VI for vfunc
+  std::map<ValueInfo, std::set<GlobalValue::GUID>> vFuncSet;
+  unsigned numSafeVFuncs = 0;
+  // Collect stats for VTables (safe, public-visibility, other).
+  std::set<ValueInfo> vTablePublicVis;
+  std::set<ValueInfo> vTableOther;
+  for (auto &TidSummary : Summary.typeIdCompatibleVtableMap()) {
+    for (const TypeIdOffsetVtableInfo &P : TidSummary.second) {
+      LLVM_DEBUG(dbgs() << "TId-vTable " << TidSummary.first << " "
+                        << P.VTableVI.name() << " " << P.AddressPointOffset
+                        << "\n");
+      bool isSafe = false;
+      const GlobalVarSummary *VS = getVTableFuncsForTId(P, isSafe);
+      if (!isSafe && VS)
+        vTablePublicVis.insert(P.VTableVI);
+      if ((isSafe && !VS) || (!isSafe && !VS))
+        vTableOther.insert(P.VTableVI);
+      if (!isSafe || !VS) {
+        continue;
+      }
+
+      // Go through VS->vTableFuncs
+      for (auto VTP : VS->vTableFuncs()) {
+        if (vFuncSet.find(P.VTableVI) == vFuncSet.end() ||
+            !vFuncSet[P.VTableVI].count(VTP.FuncVI.getGUID())) {
+          VFESafeVTablesAndFns[P.VTableVI].push_back(VTP);
+          LLVM_DEBUG(dbgs()
+                     << "vTable " << P.VTableVI.name() << " "
+                     << VTP.FuncVI.name() << " " << VTP.VTableOffset << "\n");
+          ++numSafeVFuncs;
+        }
+        vFuncSet[P.VTableVI].insert(VTP.FuncVI.getGUID());
+      }
+    }
+  }
+  llvm::errs() << "VFEThinLTO number of vTables: " << vFuncSet.size() << " "
+               << vTablePublicVis.size() << " " << vTableOther.size() << "\n";
+  llvm::errs() << "VFEThinLTO numSafeVFuncs: " << numSafeVFuncs << "\n";
+}
+
+static void checkVTableLoadsIndex(
+    ModuleSummaryIndex &Summary,
+    DenseMap<GlobalValue::GUID, std::vector<StringRef>> &NameByGUID,
+    std::map<ValueInfo, std::vector<VirtFuncOffset>> &VFESafeVTablesAndFns,
+    std::map<ValueInfo, std::vector<ValueInfo>> &VFuncsAndCallers) {
+  // Go through Function summarys for intrinsics, also funcHasNonVTableRef to
+  // erase entries from VFESafeVTableAndFns.
+  for (auto &PI : Summary) {
+    for (auto &S : PI.second.SummaryList) {
+      auto *FS = dyn_cast<FunctionSummary>(S.get());
+      if (!FS)
+        continue;
+      // We should ignore Tid if there is a type.checked.load with Offset not
+      // ConstantInt. Currently ModuleSummaryAnalysis will update TypeTests.
+      for (GlobalValue::GUID G : FS->type_tests()) {
+        if (NameByGUID.find(G) == NameByGUID.end())
+          continue;
+        auto TidSummary =
+            Summary.getTypeIdCompatibleVtableSummary(NameByGUID[G][0]);
+        for (const TypeIdOffsetVtableInfo &P : *TidSummary) {
+          LLVM_DEBUG(dbgs() << "unsafe-vtable due to type_tests: "
+                            << P.VTableVI.name() << "\n");
+          VFESafeVTablesAndFns.erase(P.VTableVI);
+        }
+      }
+      // Go through vTableFuncs to find the potential callees
+      auto CheckVLoad = [&](GlobalValue::GUID TId, uint64_t Offset) {
+        if (!NameByGUID.count(TId)) {
+          return;
+        }
+        auto TidSummary =
+            Summary.getTypeIdCompatibleVtableSummary(NameByGUID[TId][0]);
+        for (const TypeIdOffsetVtableInfo &P : *TidSummary) {
+          uint64_t VTableOffset = P.AddressPointOffset;
+          bool isSafe = false;
+          const GlobalVarSummary *VS = getVTableFuncsForTId(P, isSafe);
+          if (!isSafe || !VS)
+            continue;
+          unsigned foundCnt = 0;
+          for (auto VTP : VS->vTableFuncs()) {
+            if (VTP.VTableOffset != VTableOffset + Offset)
+              continue;
+            assert(foundCnt == 0);
+            foundCnt = 1;
+            VFuncsAndCallers[VTP.FuncVI].push_back(Summary.getValueInfo(PI));
+          }
+          if (foundCnt == 0) {
+            // A vtable is unsafe if a given vtable for the typeId doesn't have
+            // the offset.
+            VFESafeVTablesAndFns.erase(P.VTableVI);
+            LLVM_DEBUG(dbgs() << "unsafe-vtable can't find offset "
+                              << P.VTableVI.name() << " "
+                              << (VTableOffset + Offset) << "\n");
+          }
+        }
+      };
+      for (FunctionSummary::VFuncId VF : FS->type_checked_load_vcalls()) {
+        CheckVLoad(VF.GUID, VF.Offset);
+      }
+      for (const FunctionSummary::ConstVCall &VC :
+           FS->type_checked_load_const_vcalls()) {
+        CheckVLoad(VC.VFunc.GUID, VC.VFunc.Offset);
+      }
+    }
+  }
+}
+
+void runVFEOnIndex(ModuleSummaryIndex &Summary,
----------------
teresajohnson wrote:

Can you add an overview of the algorithm as a comment above this?

https://github.com/llvm/llvm-project/pull/69735


More information about the cfe-commits mailing list