[llvm] [PassBuilder] Add extra ModuleInliner/SROA/SimplifyCFG/LICM to late FullLTO passes (PR #197678)

Momchil Velikov via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 27 03:30:01 PDT 2026


https://github.com/momchil-velikov updated https://github.com/llvm/llvm-project/pull/197678

>From 55e7fedfbc173e6871dfbe2d27c06d590ea99e9d Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Thu, 26 Feb 2026 17:23:41 +0000
Subject: [PATCH 1/3] [DIS-PRE-LTO-VEC] Add extra EarlySCE to late passes

---
 llvm/lib/Passes/PassBuilderPipelines.cpp         | 16 ++++++++++++++++
 llvm/test/Other/new-pm-lto-defaults.ll           |  5 +++++
 llvm/test/ThinLTO/X86/cfi-unsat.ll               |  2 --
 .../PhaseOrdering/loop-vectorize-bfi.ll          | 11 +++++------
 4 files changed, 26 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index b8c5b1eab2f97..0186393258409 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -2339,6 +2339,21 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   // Add late LTO optimization passes.
   FunctionPassManager LateFPM;
 
+  // Delete basic blocks, which optimization passes may have killed.
+  LateFPM.addPass(SimplifyCFGPass(SimplifyCFGOptions()
+                                      .convertSwitchRangeToICmp(true)
+                                      .convertSwitchToArithmetic(true)
+                                      .hoistCommonInsts(true)
+                                      .speculateUnpredictables(true)));
+
+  LateFPM.addPass(createFunctionToLoopPassAdaptor(
+      LICMPass(PTO.LicmMssaOptCap, PTO.LicmMssaNoAccForPromotionCap,
+               /*AllowSpeculation=*/true),
+      /*USeMemorySSA=*/true));
+
+  // Catch trivial redundancies
+  LateFPM.addPass(EarlyCSEPass(true /* Enable mem-ssa. */));
+
   // LoopSink pass sinks instructions hoisted by LICM, which serves as a
   // canonicalization pass that enables other optimizations. As a result,
   // LoopSink pass needs to be a very late IR pass to avoid undoing LICM
@@ -2356,6 +2371,7 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
                                       .convertSwitchToArithmetic(true)
                                       .hoistCommonInsts(true)
                                       .speculateUnpredictables(true)));
+
   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(LateFPM)));
 
   // Drop bodies of available eternally objects to improve GlobalDCE.
diff --git a/llvm/test/Other/new-pm-lto-defaults.ll b/llvm/test/Other/new-pm-lto-defaults.ll
index 14f189194d0e3..33865f8991c4f 100644
--- a/llvm/test/Other/new-pm-lto-defaults.ll
+++ b/llvm/test/Other/new-pm-lto-defaults.ll
@@ -151,6 +151,11 @@
 ; CHECK-O23-NEXT: Running pass: JumpThreadingPass on foo
 ; CHECK-O23-NEXT: Running pass: LowerTypeTestsPass
 ; CHECK-O-NEXT: Running pass: DropTypeTestsPass
+; CHECK-O23-NEXT: Running pass: SimplifyCFGPass
+; CHECK-O23-NEXT: Running pass: LoopSimplifyPass on foo
+; CHECK-O23-NEXT: Running pass: LCSSAPass on foo
+; CHECK-O23-NEXT: Running pass: LICMPass on loop %loop
+; CHECK-O23-NEXT: Running pass: EarlyCSEPass on foo
 ; CHECK-O23-NEXT: Running pass: LoopSink
 ; CHECK-O23-NEXT: Running pass: DivRemPairs
 ; CHECK-O23-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/ThinLTO/X86/cfi-unsat.ll b/llvm/test/ThinLTO/X86/cfi-unsat.ll
index 10c1dd56565d8..f88fef4f32c11 100644
--- a/llvm/test/ThinLTO/X86/cfi-unsat.ll
+++ b/llvm/test/ThinLTO/X86/cfi-unsat.ll
@@ -57,13 +57,11 @@ cont:
 
 ; CHECK-IR0: define weak_odr i32 @test
 ; CHECK-IR0-NEXT: entry:
-; CHECK-IR0-NEXT: %vtable5 =
 ; CHECK-IR0-NEXT: tail call void @llvm.trap()
 ; CHECK-IR0-NEXT: unreachable
 ; CHECK-IR0-NEXT: }
 ; CHECK-IR0: define weak_odr i32 @testb
 ; CHECK-IR0-NEXT: entry:
-; CHECK-IR0-NEXT: %vtable5 =
 ; CHECK-IR0-NEXT: tail call void @llvm.trap()
 ; CHECK-IR0-NEXT: unreachable
 ; CHECK-IR0-NEXT: }
diff --git a/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll b/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll
index baf7bf38f3351..9fc744f0beb1b 100644
--- a/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll
+++ b/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll
@@ -10,24 +10,23 @@
 define void @f(i1 %x) !prof !0 {
 ; CHECK-LABEL: define void @f(
 ; CHECK-SAME: i1 [[X:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {{.*}}{
-; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:  [[VECTOR_PH:.*]]:
 ; CHECK-NEXT:    [[DOTSCALAR:%.*]] = xor i1 [[X]], true
 ; CHECK-NEXT:    [[TMP1:%.*]] = insertelement <vscale x 2 x i1> poison, i1 [[DOTSCALAR]], i64 0
 ; CHECK-NEXT:    [[TMP2:%.*]] = shufflevector <vscale x 2 x i1> [[TMP1]], <vscale x 2 x i1> poison, <vscale x 2 x i32> zeroinitializer
 ; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
 ; CHECK:       [[VECTOR_BODY]]:
-; CHECK-NEXT:    [[EVL_BASED_IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INDEX_EVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
-; CHECK-NEXT:    [[AVL:%.*]] = phi i64 [ 65, %[[ENTRY]] ], [ [[AVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT:    [[EVL_BASED_IV:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_EVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT:    [[AVL:%.*]] = phi i64 [ 65, %[[VECTOR_PH]] ], [ [[AVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
 ; CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.experimental.get.vector.length.i64(i64 [[AVL]], i32 2, i1 true)
 ; CHECK-NEXT:    [[TMP4:%.*]] = getelementptr [8 x i8], ptr null, i64 [[EVL_BASED_IV]]
 ; CHECK-NEXT:    call void @llvm.vp.store.nxv2i64.p0(<vscale x 2 x i64> poison, ptr align 8 [[TMP4]], <vscale x 2 x i1> [[TMP2]], i32 [[TMP3]])
-; CHECK-NEXT:    call void @llvm.vp.store.nxv2i64.p0(<vscale x 2 x i64> poison, ptr align 8 [[TMP4]], <vscale x 2 x i1> [[TMP2]], i32 [[TMP3]])
 ; CHECK-NEXT:    [[TMP5:%.*]] = zext nneg i32 [[TMP3]] to i64
 ; CHECK-NEXT:    [[INDEX_EVL_NEXT]] = add nuw i64 [[EVL_BASED_IV]], [[TMP5]]
 ; CHECK-NEXT:    [[AVL_NEXT]] = sub nuw nsw i64 [[AVL]], [[TMP5]]
 ; CHECK-NEXT:    [[TMP6:%.*]] = icmp eq i64 [[AVL_NEXT]], 0
-; CHECK-NEXT:    br i1 [[TMP6]], label %[[EXIT:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP1:![0-9]+]]
-; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    br i1 [[TMP6]], label %[[EXIT1:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP1:![0-9]+]]
+; CHECK:       [[EXIT1]]:
 ; CHECK-NEXT:    ret void
 ;
 entry:

>From 5be0f6c21949341a39e23a0c92e89b5da1889d78 Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Fri, 29 May 2026 17:00:56 +0000
Subject: [PATCH 2/3] [fuxup] Remove the late EarlyCSE pass (previously added)

This pass helps some benchmarks, degrades others, overall better
without it.
---
 llvm/lib/Passes/PassBuilderPipelines.cpp              |  3 ---
 llvm/test/Other/new-pm-lto-defaults.ll                |  1 -
 llvm/test/ThinLTO/X86/cfi-unsat.ll                    |  2 ++
 .../Transforms/PhaseOrdering/loop-vectorize-bfi.ll    | 11 ++++++-----
 4 files changed, 8 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 0186393258409..d9c254b7bcfbd 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -2351,9 +2351,6 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
                /*AllowSpeculation=*/true),
       /*USeMemorySSA=*/true));
 
-  // Catch trivial redundancies
-  LateFPM.addPass(EarlyCSEPass(true /* Enable mem-ssa. */));
-
   // LoopSink pass sinks instructions hoisted by LICM, which serves as a
   // canonicalization pass that enables other optimizations. As a result,
   // LoopSink pass needs to be a very late IR pass to avoid undoing LICM
diff --git a/llvm/test/Other/new-pm-lto-defaults.ll b/llvm/test/Other/new-pm-lto-defaults.ll
index 33865f8991c4f..3e3357339e540 100644
--- a/llvm/test/Other/new-pm-lto-defaults.ll
+++ b/llvm/test/Other/new-pm-lto-defaults.ll
@@ -155,7 +155,6 @@
 ; CHECK-O23-NEXT: Running pass: LoopSimplifyPass on foo
 ; CHECK-O23-NEXT: Running pass: LCSSAPass on foo
 ; CHECK-O23-NEXT: Running pass: LICMPass on loop %loop
-; CHECK-O23-NEXT: Running pass: EarlyCSEPass on foo
 ; CHECK-O23-NEXT: Running pass: LoopSink
 ; CHECK-O23-NEXT: Running pass: DivRemPairs
 ; CHECK-O23-NEXT: Running pass: SimplifyCFGPass
diff --git a/llvm/test/ThinLTO/X86/cfi-unsat.ll b/llvm/test/ThinLTO/X86/cfi-unsat.ll
index f88fef4f32c11..10c1dd56565d8 100644
--- a/llvm/test/ThinLTO/X86/cfi-unsat.ll
+++ b/llvm/test/ThinLTO/X86/cfi-unsat.ll
@@ -57,11 +57,13 @@ cont:
 
 ; CHECK-IR0: define weak_odr i32 @test
 ; CHECK-IR0-NEXT: entry:
+; CHECK-IR0-NEXT: %vtable5 =
 ; CHECK-IR0-NEXT: tail call void @llvm.trap()
 ; CHECK-IR0-NEXT: unreachable
 ; CHECK-IR0-NEXT: }
 ; CHECK-IR0: define weak_odr i32 @testb
 ; CHECK-IR0-NEXT: entry:
+; CHECK-IR0-NEXT: %vtable5 =
 ; CHECK-IR0-NEXT: tail call void @llvm.trap()
 ; CHECK-IR0-NEXT: unreachable
 ; CHECK-IR0-NEXT: }
diff --git a/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll b/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll
index 9fc744f0beb1b..baf7bf38f3351 100644
--- a/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll
+++ b/llvm/test/Transforms/PhaseOrdering/loop-vectorize-bfi.ll
@@ -10,23 +10,24 @@
 define void @f(i1 %x) !prof !0 {
 ; CHECK-LABEL: define void @f(
 ; CHECK-SAME: i1 [[X:%.*]]) local_unnamed_addr #[[ATTR0:[0-9]+]] {{.*}}{
-; CHECK-NEXT:  [[VECTOR_PH:.*]]:
+; CHECK-NEXT:  [[ENTRY:.*]]:
 ; CHECK-NEXT:    [[DOTSCALAR:%.*]] = xor i1 [[X]], true
 ; CHECK-NEXT:    [[TMP1:%.*]] = insertelement <vscale x 2 x i1> poison, i1 [[DOTSCALAR]], i64 0
 ; CHECK-NEXT:    [[TMP2:%.*]] = shufflevector <vscale x 2 x i1> [[TMP1]], <vscale x 2 x i1> poison, <vscale x 2 x i32> zeroinitializer
 ; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
 ; CHECK:       [[VECTOR_BODY]]:
-; CHECK-NEXT:    [[EVL_BASED_IV:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_EVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
-; CHECK-NEXT:    [[AVL:%.*]] = phi i64 [ 65, %[[VECTOR_PH]] ], [ [[AVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT:    [[EVL_BASED_IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INDEX_EVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
+; CHECK-NEXT:    [[AVL:%.*]] = phi i64 [ 65, %[[ENTRY]] ], [ [[AVL_NEXT:%.*]], %[[VECTOR_BODY]] ]
 ; CHECK-NEXT:    [[TMP3:%.*]] = call i32 @llvm.experimental.get.vector.length.i64(i64 [[AVL]], i32 2, i1 true)
 ; CHECK-NEXT:    [[TMP4:%.*]] = getelementptr [8 x i8], ptr null, i64 [[EVL_BASED_IV]]
 ; CHECK-NEXT:    call void @llvm.vp.store.nxv2i64.p0(<vscale x 2 x i64> poison, ptr align 8 [[TMP4]], <vscale x 2 x i1> [[TMP2]], i32 [[TMP3]])
+; CHECK-NEXT:    call void @llvm.vp.store.nxv2i64.p0(<vscale x 2 x i64> poison, ptr align 8 [[TMP4]], <vscale x 2 x i1> [[TMP2]], i32 [[TMP3]])
 ; CHECK-NEXT:    [[TMP5:%.*]] = zext nneg i32 [[TMP3]] to i64
 ; CHECK-NEXT:    [[INDEX_EVL_NEXT]] = add nuw i64 [[EVL_BASED_IV]], [[TMP5]]
 ; CHECK-NEXT:    [[AVL_NEXT]] = sub nuw nsw i64 [[AVL]], [[TMP5]]
 ; CHECK-NEXT:    [[TMP6:%.*]] = icmp eq i64 [[AVL_NEXT]], 0
-; CHECK-NEXT:    br i1 [[TMP6]], label %[[EXIT1:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP1:![0-9]+]]
-; CHECK:       [[EXIT1]]:
+; CHECK-NEXT:    br i1 [[TMP6]], label %[[EXIT:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP1:![0-9]+]]
+; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
 ;
 entry:

>From 8527b4fddddbce3b28a7e595e872e7fc389c5e37 Mon Sep 17 00:00:00 2001
From: Momchil Velikov <momchil.velikov at arm.com>
Date: Tue, 16 Jun 2026 15:48:37 +0000
Subject: [PATCH 3/3] [fixup] Add late inliner and SROA passes to the LTO
 pipeline

Not running SLP before link-time inliner causes the compiler to
miss inline opportunities which in turn prevents SROA from
optimising certain important cases when aggregates are passed as
parameters or return values. It has been observed to cause
8-9% regression in some benchmarks.
---
 llvm/lib/Passes/PassBuilderPipelines.cpp | 15 ++++++++++++++-
 llvm/test/Other/new-pm-lto-defaults.ll   |  6 ++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index d9c254b7bcfbd..bdd7851607bd3 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -2323,6 +2323,18 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(MainFPM),
                                                 PTO.EagerlyInvalidateAnalyses));
 
+  if (EnableModuleInliner) {
+    MPM.addPass(ModuleInlinerPass(::getInlineParamsFromOptLevel(Level),
+                                  UseInlineAdvisor,
+                                  ThinOrFullLTOPhase::FullLTOPostLink));
+  } else {
+    MPM.addPass(ModuleInlinerWrapperPass(
+        ::getInlineParamsFromOptLevel(Level),
+        /* MandatoryFirst */ true,
+        InlineContext{ThinOrFullLTOPhase::FullLTOPostLink,
+                      InlinePass::CGSCCInliner}));
+  }
+
   // Lower type metadata and the type.test intrinsic. This pass supports
   // clang's control flow integrity mechanisms (-fsanitize=cfi*) and needs
   // to be run at link time if CFI is enabled. This pass does nothing if
@@ -2339,6 +2351,8 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
   // Add late LTO optimization passes.
   FunctionPassManager LateFPM;
 
+  LateFPM.addPass(SROAPass(SROAOptions::ModifyCFG));
+
   // Delete basic blocks, which optimization passes may have killed.
   LateFPM.addPass(SimplifyCFGPass(SimplifyCFGOptions()
                                       .convertSwitchRangeToICmp(true)
@@ -2368,7 +2382,6 @@ PassBuilder::buildLTODefaultPipeline(OptimizationLevel Level,
                                       .convertSwitchToArithmetic(true)
                                       .hoistCommonInsts(true)
                                       .speculateUnpredictables(true)));
-
   MPM.addPass(createModuleToFunctionPassAdaptor(std::move(LateFPM)));
 
   // Drop bodies of available eternally objects to improve GlobalDCE.
diff --git a/llvm/test/Other/new-pm-lto-defaults.ll b/llvm/test/Other/new-pm-lto-defaults.ll
index 3e3357339e540..79b95b6ce8691 100644
--- a/llvm/test/Other/new-pm-lto-defaults.ll
+++ b/llvm/test/Other/new-pm-lto-defaults.ll
@@ -149,8 +149,14 @@
 ; CHECK-EP-VECTORIZER-END-NEXT: Running pass: NoOpFunctionPass on foo
 ; CHECK-EP-PEEPHOLE-NEXT: Running pass: NoOpFunctionPass on foo
 ; CHECK-O23-NEXT: Running pass: JumpThreadingPass on foo
+; CHECK-O23-NEXT: Running pass: ModuleInlinerWrapperPass on [module]
+; CHECK-O23-NEXT: Running analysis: InlineAdvisorAnalysis on [module]
+; CHECK-O23-NEXT: Running pass: InlinerPass on (foo) (1 node)
+; CHECK-O23-NEXT: Running pass: InlinerPass on (foo) (1 node)
+; CHECK-O23-NEXT: Invalidating analysis: InlineAdvisorAnalysis on [module]
 ; CHECK-O23-NEXT: Running pass: LowerTypeTestsPass
 ; CHECK-O-NEXT: Running pass: DropTypeTestsPass
+; CHECK-O23-NEXT: Running pass: SROAPass on foo
 ; CHECK-O23-NEXT: Running pass: SimplifyCFGPass
 ; CHECK-O23-NEXT: Running pass: LoopSimplifyPass on foo
 ; CHECK-O23-NEXT: Running pass: LCSSAPass on foo



More information about the llvm-commits mailing list