[clang] [llvm] Enable WPD without lto (PR #141777)

Hassnaa Hamdi via cfe-commits cfe-commits at lists.llvm.org
Wed May 28 07:34:37 PDT 2025


https://github.com/hassnaaHamdi created https://github.com/llvm/llvm-project/pull/141777

None

>From 702f64a84914d2fe467a12babd99338f2215d425 Mon Sep 17 00:00:00 2001
From: Hassnaa Hamdi <hassnaa.hamdi at arm.com>
Date: Wed, 28 May 2025 14:27:34 +0000
Subject: [PATCH] Enable WPD without lto

---
 clang/lib/CodeGen/CGVTables.cpp               |  3 +-
 clang/lib/Driver/ToolChains/Clang.cpp         |  8 +-
 llvm/lib/Passes/PassBuilderPipelines.cpp      |  3 +
 .../lib/Transforms/IPO/WholeProgramDevirt.cpp | 83 +++++++++++--------
 4 files changed, 61 insertions(+), 36 deletions(-)

diff --git a/clang/lib/CodeGen/CGVTables.cpp b/clang/lib/CodeGen/CGVTables.cpp
index c7447273a42fa..48036df7b7b6f 100644
--- a/clang/lib/CodeGen/CGVTables.cpp
+++ b/clang/lib/CodeGen/CGVTables.cpp
@@ -1359,7 +1359,8 @@ void CodeGenModule::EmitVTableTypeMetadata(const CXXRecordDecl *RD,
   // Emit type metadata on vtables with LTO or IR instrumentation.
   // In IR instrumentation, the type metadata is used to find out vtable
   // definitions (for type profiling) among all global variables.
-  if (!getCodeGenOpts().LTOUnit && !getCodeGenOpts().hasProfileIRInstr())
+  if (!getCodeGenOpts().LTOUnit && !getCodeGenOpts().hasProfileIRInstr() &&
+      !getCodeGenOpts().WholeProgramVTables)
     return;
 
   CharUnits ComponentWidth = GetTargetTypeStoreSize(getVTableComponentType());
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index 13842b8cc2870..bdb7fc9b778d6 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7970,8 +7970,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
         IsDeviceOffloadAction ? D.getLTOMode() : D.getOffloadLTOMode();
     auto OtherIsUsingLTO = OtherLTOMode != LTOK_None;
 
-    if ((!IsUsingLTO && !OtherIsUsingLTO) ||
-        (IsPS4 && !UnifiedLTO && (D.getLTOMode() != LTOK_Full)))
+    if (!IsUsingLTO && !OtherIsUsingLTO && !UnifiedLTO) {
+      if (Arg *A = Args.getLastArg(options::OPT_O_Group))
+        if (!A->getOption().matches(options::OPT_O0))
+          CmdArgs.push_back("-fwhole-program-vtables");
+    } else if ((!IsUsingLTO && !OtherIsUsingLTO) ||
+               (IsPS4 && !UnifiedLTO && (D.getLTOMode() != LTOK_Full)))
       D.Diag(diag::err_drv_argument_only_allowed_with)
           << "-fwhole-program-vtables"
           << ((IsPS4 && !UnifiedLTO) ? "-flto=full" : "-flto");
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index f3654600c5abb..0d021cb7c47c3 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -1295,6 +1295,9 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
   MPM.addPass(GlobalOptPass());
   MPM.addPass(GlobalDCEPass());
 
+  if (Phase == ThinOrFullLTOPhase::None && Level != OptimizationLevel::O0)
+    MPM.addPass(WholeProgramDevirtPass(nullptr, nullptr));
+
   return MPM;
 }
 
diff --git a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
index 3a25255d0a4c8..44c2b58f3b735 100644
--- a/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
+++ b/llvm/lib/Transforms/IPO/WholeProgramDevirt.cpp
@@ -60,7 +60,9 @@
 #include "llvm/ADT/Statistic.h"
 #include "llvm/Analysis/AssumptionCache.h"
 #include "llvm/Analysis/BasicAliasAnalysis.h"
+#include "llvm/Analysis/ModuleSummaryAnalysis.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
+#include "llvm/Analysis/ProfileSummaryInfo.h"
 #include "llvm/Analysis/TypeMetadataUtils.h"
 #include "llvm/Bitcode/BitcodeReader.h"
 #include "llvm/Bitcode/BitcodeWriter.h"
@@ -209,7 +211,8 @@ static cl::opt<WPDCheckMode> DevirtCheckMode(
     cl::values(clEnumValN(WPDCheckMode::None, "none", "No checking"),
                clEnumValN(WPDCheckMode::Trap, "trap", "Trap when incorrect"),
                clEnumValN(WPDCheckMode::Fallback, "fallback",
-                          "Fallback to indirect when incorrect")));
+                          "Fallback to indirect when incorrect")),
+    cl::init(WPDCheckMode::Fallback));
 
 namespace {
 struct PatternList {
@@ -804,6 +807,12 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
       return PreservedAnalyses::all();
     return PreservedAnalyses::none();
   }
+  if (!ExportSummary) {
+    ProfileSummaryInfo PSI(M);
+    std::optional<ModuleSummaryIndex> Index;
+    Index.emplace(buildModuleSummaryIndex(M, nullptr, &PSI));
+    ExportSummary = Index.has_value() ? &Index.value() : nullptr;
+  }
   if (!DevirtModule(M, AARGetter, OREGetter, LookupDomTree, ExportSummary,
                     ImportSummary)
            .run())
@@ -814,8 +823,10 @@ PreservedAnalyses WholeProgramDevirtPass::run(Module &M,
 // Enable whole program visibility if enabled by client (e.g. linker) or
 // internal option, and not force disabled.
 bool llvm::hasWholeProgramVisibility(bool WholeProgramVisibilityEnabledInLTO) {
-  return (WholeProgramVisibilityEnabledInLTO || WholeProgramVisibility) &&
-         !DisableWholeProgramVisibility;
+  if (WholeProgramVisibilityEnabledInLTO)
+    return (WholeProgramVisibilityEnabledInLTO || WholeProgramVisibility) &&
+           !DisableWholeProgramVisibility;
+  return true;
 }
 
 static bool
@@ -1099,9 +1110,9 @@ bool DevirtModule::tryFindVirtualCallTargets(
 
     // We cannot perform whole program devirtualization analysis on a vtable
     // with public LTO visibility.
-    if (TM.Bits->GV->getVCallVisibility() ==
-        GlobalObject::VCallVisibilityPublic)
-      return false;
+    // if (TM.Bits->GV->getVCallVisibility() ==
+    //     GlobalObject::VCallVisibilityPublic)
+    //   return false;
 
     Function *Fn = nullptr;
     Constant *C = nullptr;
@@ -1342,26 +1353,28 @@ bool DevirtModule::trySingleImplDevirt(
   // If the only implementation has local linkage, we must promote to external
   // 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() + ".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
-    // must match one of the names of the symbols in the comdat.
-    if (Comdat *C = TheFn->getComdat()) {
-      if (C->getName() == TheFn->getName()) {
-        Comdat *NewC = M.getOrInsertComdat(NewName);
-        NewC->setSelectionKind(C->getSelectionKind());
-        for (GlobalObject &GO : M.global_objects())
-          if (GO.getComdat() == C)
-            GO.setComdat(NewC);
-      }
-    }
-
-    TheFn->setLinkage(GlobalValue::ExternalLinkage);
-    TheFn->setVisibility(GlobalValue::HiddenVisibility);
-    TheFn->setName(NewName);
-  }
+  // if (TheFn->hasLocalLinkage()) {
+  //   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
+  //   // must match one of the names of the symbols in the comdat.
+  //   if (Comdat *C = TheFn->getComdat()) {
+  //     if (C->getName() == TheFn->getName()) {
+  //       Comdat *NewC = M.getOrInsertComdat(NewName);
+  //       NewC->setSelectionKind(C->getSelectionKind());
+  //       for (GlobalObject &GO : M.global_objects())
+  //         if (GO.getComdat() == C)
+  //           GO.setComdat(NewC);
+  //     }
+  //   }
+
+  //   TheFn->setLinkage(GlobalValue::ExternalLinkage);
+  //   TheFn->setVisibility(GlobalValue::HiddenVisibility);
+  //   TheFn->setName(NewName);
+  // }
   if (ValueInfo TheFnVI = ExportSummary->getValueInfo(TheFn->getGUID()))
     // Any needed promotion of 'TheFn' has already been done during
     // LTO unit split, so we can ignore return value of AddCalls.
@@ -2321,6 +2334,9 @@ bool DevirtModule::run() {
 
   Function *TypeTestFunc =
       Intrinsic::getDeclarationIfExists(&M, Intrinsic::type_test);
+  if (!TypeTestFunc)
+    TypeTestFunc =
+        Intrinsic::getDeclarationIfExists(&M, Intrinsic::public_type_test);
   Function *TypeCheckedLoadFunc =
       Intrinsic::getDeclarationIfExists(&M, Intrinsic::type_checked_load);
   Function *TypeCheckedLoadRelativeFunc = Intrinsic::getDeclarationIfExists(
@@ -2443,13 +2459,14 @@ bool DevirtModule::run() {
                  .WPDRes[S.first.ByteOffset];
     if (tryFindVirtualCallTargets(TargetsForSlot, TypeMemberInfos,
                                   S.first.ByteOffset, ExportSummary)) {
-
-      if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res)) {
-        DidVirtualConstProp |=
-            tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first);
-
-        tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first);
-      }
+      trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second, Res);
+      // Following features are not needed for the case of enabling WPD without
+      // lto. if (!trySingleImplDevirt(ExportSummary, TargetsForSlot, S.second,
+      // Res)) { DidVirtualConstProp |=
+      //     tryVirtualConstProp(TargetsForSlot, S.second, Res, S.first);
+
+      //   tryICallBranchFunnel(TargetsForSlot, S.second, Res, S.first);
+      // }
 
       // Collect functions devirtualized at least for one call site for stats.
       if (RemarksEnabled || AreStatisticsEnabled())



More information about the cfe-commits mailing list