[llvm] [LTO] Enable `PrepareForLTO` for loop unrolling in pre-link pipelines (PR #192155)
Justin Fargnoli via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 27 08:46:54 PDT 2026
https://github.com/justinfargnoli updated https://github.com/llvm/llvm-project/pull/192155
>From fb78adcde96166af530331504191770aff96a3bd Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Wed, 15 Apr 2026 00:13:15 +0000
Subject: [PATCH 1/2] [LTO] Enable PrepareForLTO for loop unrolling in pre-link
pipelines
Set PrepareForLTO=true on the LoopFullUnrollPass in the O1 and O2
function simplification pipelines when running in a ThinLTO or
FullLTO pre-link phase. This defers unrolling of loops with calls
that may be inlined during the LTO link phase, allowing the post-link
unroller to make better decisions with fully resolved call targets.
---
llvm/lib/Passes/PassBuilderPipelines.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index f017419791a85..f486c56a92b1e 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -538,7 +538,8 @@ PassBuilder::buildO1FunctionSimplificationPipeline(OptimizationLevel Level,
PGOOpt->Action != PGOOptions::SampleUse)
LPM2.addPass(LoopFullUnrollPass(Level.getSpeedupLevel(),
/* OnlyWhenForced= */ !PTO.LoopUnrolling,
- PTO.ForgetAllSCEVInLoopUnroll));
+ PTO.ForgetAllSCEVInLoopUnroll,
+ /* PrepareForLTO= */ isLTOPreLink(Phase)));
invokeLoopOptimizerEndEPCallbacks(LPM2, Level);
@@ -722,7 +723,8 @@ PassBuilder::buildFunctionSimplificationPipeline(OptimizationLevel Level,
PGOOpt->Action != PGOOptions::SampleUse)
LPM2.addPass(LoopFullUnrollPass(Level.getSpeedupLevel(),
/* OnlyWhenForced= */ !PTO.LoopUnrolling,
- PTO.ForgetAllSCEVInLoopUnroll));
+ PTO.ForgetAllSCEVInLoopUnroll,
+ /* PrepareForLTO= */ isLTOPreLink(Phase)));
invokeLoopOptimizerEndEPCallbacks(LPM2, Level);
>From 8369a6f7971aef8872432cc48479be61c89c63b0 Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Mon, 20 Jul 2026 21:54:30 +0000
Subject: [PATCH 2/2] Finish up PR
---
llvm/lib/Passes/PassBuilderPipelines.cpp | 8 +--
.../new-pm-lto-prelink-prepare-for-lto.ll | 50 +++++++++++++++++++
2 files changed, 55 insertions(+), 3 deletions(-)
create mode 100644 llvm/test/Other/new-pm-lto-prelink-prepare-for-lto.ll
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 05e0cebde9293..67a534e6e2acb 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -1472,9 +1472,11 @@ void PassBuilder::addVectorPasses(OptimizationLevel Level,
FPM.addPass(createFunctionToLoopPassAdaptor(
LoopUnrollAndJamPass(static_cast<int>(Level))));
}
- FPM.addPass(LoopUnrollPass(LoopUnrollOptions(
- static_cast<int>(Level), /*OnlyWhenForced=*/!PTO.LoopUnrolling,
- PTO.ForgetAllSCEVInLoopUnroll)));
+ FPM.addPass(
+ LoopUnrollPass(LoopUnrollOptions(static_cast<int>(Level),
+ /*OnlyWhenForced=*/!PTO.LoopUnrolling,
+ PTO.ForgetAllSCEVInLoopUnroll)
+ .setPrepareForLTO(isLTOPreLink(LTOPhase))));
FPM.addPass(WarnMissedTransformationsPass());
// Now that we are done with loop unrolling, be it either by LoopVectorizer,
// or LoopUnroll passes, some variable-offset GEP's into alloca's could have
diff --git a/llvm/test/Other/new-pm-lto-prelink-prepare-for-lto.ll b/llvm/test/Other/new-pm-lto-prelink-prepare-for-lto.ll
new file mode 100644
index 0000000000000..bcd2b339f2b49
--- /dev/null
+++ b/llvm/test/Other/new-pm-lto-prelink-prepare-for-lto.ll
@@ -0,0 +1,50 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='thinlto-pre-link<O2>' %s | FileCheck %s --check-prefix=PRELINK
+; RUN: opt -S -passes='lto-pre-link<O2>' %s | FileCheck %s --check-prefix=PRELINK
+; RUN: opt -S -passes='thinlto<O2>' %s | FileCheck %s --check-prefix=POSTLINK
+; RUN: opt -S -passes='lto<O2>' %s | FileCheck %s --check-prefix=POSTLINK
+
+; Check that the LTO pre-link pipelines defer full unrolling when a loop
+; contains a call that may be inlined at link time, while the post-link
+; pipelines fully unroll it.
+
+declare void @callee(i32)
+
+define void @caller() {
+; PRELINK-LABEL: define void @caller(
+; PRELINK-SAME: ) local_unnamed_addr !guid [[META0:![0-9]+]] {
+; PRELINK-NEXT: [[ENTRY:.*]]:
+; PRELINK-NEXT: br label %[[LOOP:.*]]
+; PRELINK: [[LOOP]]:
+; PRELINK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[INC:%.*]], %[[LOOP]] ]
+; PRELINK-NEXT: tail call void @callee(i32 [[IV]])
+; PRELINK-NEXT: [[INC]] = add nuw nsw i32 [[IV]], 1
+; PRELINK-NEXT: [[DONE:%.*]] = icmp eq i32 [[INC]], 4
+; PRELINK-NEXT: br i1 [[DONE]], label %[[EXIT:.*]], label %[[LOOP]]
+; PRELINK: [[EXIT]]:
+; PRELINK-NEXT: ret void
+;
+; POSTLINK-LABEL: define void @caller() local_unnamed_addr {
+; POSTLINK-NEXT: [[ENTRY:.*:]]
+; POSTLINK-NEXT: tail call void @callee(i32 0)
+; POSTLINK-NEXT: tail call void @callee(i32 1)
+; POSTLINK-NEXT: tail call void @callee(i32 2)
+; POSTLINK-NEXT: tail call void @callee(i32 3)
+; POSTLINK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %inc, %loop ]
+ call void @callee(i32 %iv)
+ %inc = add i32 %iv, 1
+ %done = icmp eq i32 %inc, 4
+ br i1 %done, label %exit, label %loop
+
+exit:
+ ret void
+}
+;.
+; PRELINK: [[META0]] = !{i64 -1768971689307247648}
+;.
More information about the llvm-commits
mailing list