[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