[llvm] Add code to disable scalar PRE in GVN (PR #190386)

via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 3 12:05:21 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-backend-nvptx

Author: Daniel Donenfeld (daniel-donenfeld)

<details>
<summary>Changes</summary>

Scalar PRE in GVN causes performance issues in the NVPTX backend by increasing register pressure. This PR introduces a flag to disable it and uses the flag in for NVPTX.

---
Full diff: https://github.com/llvm/llvm-project/pull/190386.diff


6 Files Affected:

- (modified) llvm/include/llvm/Transforms/Scalar/GVN.h (+9) 
- (modified) llvm/lib/Passes/PassBuilder.cpp (+2) 
- (modified) llvm/lib/Passes/PassRegistry.def (+1-1) 
- (modified) llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp (+2-1) 
- (modified) llvm/lib/Transforms/Scalar/GVN.cpp (+25-9) 
- (added) llvm/test/Transforms/GVN/PRE/no-scalar-pre.ll (+26) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/Scalar/GVN.h b/llvm/include/llvm/Transforms/Scalar/GVN.h
index f79896e993d9b..ccc608eb0c42b 100644
--- a/llvm/include/llvm/Transforms/Scalar/GVN.h
+++ b/llvm/include/llvm/Transforms/Scalar/GVN.h
@@ -77,6 +77,7 @@ class GVNLegacyPass;
 /// additional setters and then pass it to GVN.
 struct GVNOptions {
   std::optional<bool> AllowPRE;
+  std::optional<bool> AllowScalarPRE;
   std::optional<bool> AllowLoadPRE;
   std::optional<bool> AllowLoadInLoopPRE;
   std::optional<bool> AllowLoadPRESplitBackedge;
@@ -91,6 +92,12 @@ struct GVNOptions {
     return *this;
   }
 
+  /// Enables or disables PRE of scalars in GVN.
+  GVNOptions &setScalarPRE(bool ScalarPRE) {
+    AllowScalarPRE = ScalarPRE;
+    return *this;
+  }
+
   /// Enables or disables PRE of loads in GVN.
   GVNOptions &setLoadPRE(bool LoadPRE) {
     AllowLoadPRE = LoadPRE;
@@ -149,6 +156,7 @@ class GVNPass : public PassInfoMixin<GVNPass> {
   MemoryDependenceResults &getMemDep() const { return *MD; }
 
   LLVM_ABI bool isPREEnabled() const;
+  LLVM_ABI bool isScalarPREEnabled() const;
   LLVM_ABI bool isLoadPREEnabled() const;
   LLVM_ABI bool isLoadInLoopPREEnabled() const;
   LLVM_ABI bool isLoadPRESplitBackedgeEnabled() const;
@@ -408,6 +416,7 @@ class GVNPass : public PassInfoMixin<GVNPass> {
 };
 
 /// Create a legacy GVN pass.
+LLVM_ABI FunctionPass *createGVNPass(bool ScalarPRE);
 LLVM_ABI FunctionPass *createGVNPass();
 
 /// A simple and fast domtree-based GVN pass to hoist common expressions
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 55a4e99e7402e..25d5ac2e1e446 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -1331,6 +1331,8 @@ Expected<GVNOptions> parseGVNOptions(StringRef Params) {
     bool Enable = !ParamName.consume_front("no-");
     if (ParamName == "pre") {
       Result.setPRE(Enable);
+    } else if (ParamName == "scalar-pre") {
+      Result.setScalarPRE(Enable);
     } else if (ParamName == "load-pre") {
       Result.setLoadPRE(Enable);
     } else if (ParamName == "split-backedge-load-pre") {
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index c92d93d7ae396..c64bf9bf729b0 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -598,7 +598,7 @@ FUNCTION_PASS_WITH_PARAMS(
 FUNCTION_PASS_WITH_PARAMS(
     "gvn", "GVNPass", [](GVNOptions Opts) { return GVNPass(Opts); },
     parseGVNOptions,
-    "no-pre;pre;no-load-pre;load-pre;no-split-backedge-load-pre;"
+    "no-pre;pre;no-scalar-pre;scalar-pre;no-load-pre;load-pre;no-split-backedge-load-pre;"
     "split-backedge-load-pre;no-memdep;memdep;no-memoryssa;memoryssa")
 FUNCTION_PASS_WITH_PARAMS(
     "hardware-loops", "HardwareLoopsPass",
diff --git a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
index 10e746c502c09..9351c8dde60d4 100644
--- a/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
+++ b/llvm/lib/Target/NVPTX/NVPTXTargetMachine.cpp
@@ -291,7 +291,8 @@ NVPTXTargetMachine::getPredicatedAddrSpace(const Value *V) const {
 
 void NVPTXPassConfig::addEarlyCSEOrGVNPass() {
   if (getOptLevel() == CodeGenOptLevel::Aggressive)
-    addPass(createGVNPass());
+    // Disable scalar PRE due to Register Pressure increase
+    addPass(createGVNPass(/*ScalarPRE=*/false));
   else
     addPass(createEarlyCSEPass());
 }
diff --git a/llvm/lib/Transforms/Scalar/GVN.cpp b/llvm/lib/Transforms/Scalar/GVN.cpp
index 7cab4be169123..1a592ff160950 100644
--- a/llvm/lib/Transforms/Scalar/GVN.cpp
+++ b/llvm/lib/Transforms/Scalar/GVN.cpp
@@ -106,6 +106,8 @@ STATISTIC(MaxBBSpeculationCutoffReachedTimes,
           "preventing further exploration");
 
 static cl::opt<bool> GVNEnablePRE("enable-pre", cl::init(true), cl::Hidden);
+static cl::opt<bool> GVNEnableScalarPRE("enable-scalar-pre", cl::init(true),
+                                        cl::Hidden);
 static cl::opt<bool> GVNEnableLoadPRE("enable-load-pre", cl::init(true));
 static cl::opt<bool> GVNEnableLoadInLoopPRE("enable-load-in-loop-pre",
                                             cl::init(true));
@@ -854,6 +856,10 @@ bool GVNPass::isPREEnabled() const {
   return Options.AllowPRE.value_or(GVNEnablePRE);
 }
 
+bool GVNPass::isScalarPREEnabled() const {
+  return Options.AllowScalarPRE.value_or(GVNEnableScalarPRE);
+}
+
 bool GVNPass::isLoadPREEnabled() const {
   return Options.AllowLoadPRE.value_or(GVNEnableLoadPRE);
 }
@@ -915,6 +921,8 @@ void GVNPass::printPipeline(
   OS << '<';
   if (Options.AllowPRE != std::nullopt)
     OS << (*Options.AllowPRE ? "" : "no-") << "pre;";
+  if (Options.AllowScalarPRE != std::nullopt)
+    OS << (*Options.AllowScalarPRE ? "" : "no-") << "scalar-pre;";
   if (Options.AllowLoadPRE != std::nullopt)
     OS << (*Options.AllowLoadPRE ? "" : "no-") << "load-pre;";
   if (Options.AllowLoadPRESplitBackedge != std::nullopt)
@@ -2029,12 +2037,15 @@ bool GVNPass::processNonLocalLoad(LoadInst *Load) {
   }
 
   bool Changed = false;
-  // If this load follows a GEP, see if we can PRE the indices before analyzing.
-  if (GetElementPtrInst *GEP =
-          dyn_cast<GetElementPtrInst>(Load->getOperand(0))) {
-    for (Use &U : GEP->indices())
-      if (Instruction *I = dyn_cast<Instruction>(U.get()))
-        Changed |= performScalarPRE(I);
+  // This is a limited form of scalar PRE for load indices. If this load follows
+  // a GEP, see if we can PRE the indices before analyzing.
+  if (isPREEnabled() && isScalarPREEnabled()) {
+    if (GetElementPtrInst *GEP =
+            dyn_cast<GetElementPtrInst>(Load->getOperand(0))) {
+      for (Use &U : GEP->indices())
+        if (Instruction *I = dyn_cast<Instruction>(U.get()))
+          Changed |= performScalarPRE(I);
+    }
   }
 
   // Step 2: Analyze the availability of the load.
@@ -2842,7 +2853,7 @@ bool GVNPass::runImpl(Function &F, AssumptionCache &RunAC, DominatorTree &RunDT,
     ++Iteration;
   }
 
-  if (isPREEnabled()) {
+  if (isPREEnabled() && isScalarPREEnabled()) {
     // Fabricate val-num for dead-code in order to suppress assertion in
     // performPRE().
     assignValNumForDeadCode();
@@ -3345,10 +3356,12 @@ class llvm::gvn::GVNLegacyPass : public FunctionPass {
   static char ID; // Pass identification, replacement for typeid.
 
   explicit GVNLegacyPass(bool MemDepAnalysis = GVNEnableMemDep,
-                         bool MemSSAAnalysis = GVNEnableMemorySSA)
+                         bool MemSSAAnalysis = GVNEnableMemorySSA,
+                         bool ScalarPRE = true)
       : FunctionPass(ID), Impl(GVNOptions()
                                    .setMemDep(MemDepAnalysis)
-                                   .setMemorySSA(MemSSAAnalysis)) {
+                                   .setMemorySSA(MemSSAAnalysis)
+                                   .setScalarPRE(ScalarPRE)) {
     initializeGVNLegacyPassPass(*PassRegistry::getPassRegistry());
   }
 
@@ -3410,3 +3423,6 @@ INITIALIZE_PASS_END(GVNLegacyPass, "gvn", "Global Value Numbering", false, false
 
 // The public interface to this file...
 FunctionPass *llvm::createGVNPass() { return new GVNLegacyPass(); }
+FunctionPass *llvm::createGVNPass(bool ScalarPRE) {
+  return new GVNLegacyPass(GVNEnableMemDep, GVNEnableMemorySSA, ScalarPRE);
+}
diff --git a/llvm/test/Transforms/GVN/PRE/no-scalar-pre.ll b/llvm/test/Transforms/GVN/PRE/no-scalar-pre.ll
new file mode 100644
index 0000000000000..f11e9dba2bd6b
--- /dev/null
+++ b/llvm/test/Transforms/GVN/PRE/no-scalar-pre.ll
@@ -0,0 +1,26 @@
+; RUN: opt -enable-scalar-pre=false -enable-pre -passes=gvn -S < %s | FileCheck %s --check-prefixes=CHECK
+
+define void @kernel(ptr %arr, i8 %cond) {
+entry:
+  %tobool.not = icmp eq i8 %cond, 0
+  %tmp7.pre = load i32, ptr %arr, align 4
+  br i1 %tobool.not, label %if.end, label %if.then
+
+; CHECK: if.then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[LOAD:%.*]], 2
+
+if.then:                                          ; preds = %entry
+  %add = add nsw i32 %tmp7.pre, 2
+  %getElem = getelementptr inbounds nuw i8, ptr %arr, i64 8
+  store i32 %add, ptr %getElem, align 4
+  br label %if.end
+
+; CHECK: if.end:
+; CHECK-NEXT: [[ADD2:%.*]] = add nsw i32 [[LOAD:%.*]], 2
+
+if.end:                                           ; preds = %if.then, %entry
+  %add8 = add nsw i32 %tmp7.pre, 2
+  %getElem1 = getelementptr inbounds nuw i8, ptr %arr, i64 12
+  store i32 %add8, ptr %getElem1, align 4
+  ret void
+}
\ No newline at end of file

``````````

</details>


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


More information about the llvm-commits mailing list