[llvm] [LoopUnroll] Add `prepare-for-lto` pass option (PR #192153)

via llvm-commits llvm-commits at lists.llvm.org
Tue Apr 14 23:45:51 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Justin Fargnoli (justinfargnoli)

<details>
<summary>Changes</summary>

## Summary
- Expose `UnrollCostEstimator`'s `PrepareForLTO` flag as a pass option for `LoopUnrollPass` and `LoopFullUnrollPass`
- When enabled, loops containing calls that may be inlined during the LTO post-link phase are left rolled in the pre-link phase, deferring the decision until LTO post-link inlining has completed
- Adds pass pipeline option `prepare-for-lto` for `loop-unroll` (e.g. `loop-unroll<prepare-for-lto>`)
- `LoopFullUnrollPass`'s `PrepareForLTO` is set programmatically by the pipeline builder (not exposed via `-passes=loop-unroll-full<...>`)
- The underlying infrastructure already existed in `CodeMetrics` and was used by `LoopRotatePass`; this wires it up for loop unrolling as well

## Test plan
- `llvm/test/Transforms/LoopUnroll/prepare-for-lto.ll` — behavioral test with auto-generated CHECK lines showing unrolling deferred for loops with extern calls under `prepare-for-lto`
- `llvm/test/Other/new-pm-print-pipeline.ll` — verifies the option round-trips through `printPipeline`
- `llvm/test/Other/print-passes.ll` — verifies the option appears in `--print-passes` output

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


8 Files Affected:

- (modified) llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h (+16-2) 
- (modified) llvm/include/llvm/Transforms/Utils/UnrollLoop.h (+1-1) 
- (modified) llvm/lib/Passes/PassBuilder.cpp (+2) 
- (modified) llvm/lib/Passes/PassRegistry.def (+1-1) 
- (modified) llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp (+15-11) 
- (modified) llvm/test/Other/new-pm-print-pipeline.ll (+2-2) 
- (modified) llvm/test/Other/print-passes.ll (+1-1) 
- (added) llvm/test/Transforms/LoopUnroll/prepare-for-lto.ll (+165) 


``````````diff
diff --git a/llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h b/llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h
index 8d8c2f254f02f..4279494d5180c 100644
--- a/llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h
+++ b/llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h
@@ -36,11 +36,16 @@ class LoopFullUnrollPass : public PassInfoMixin<LoopFullUnrollPass> {
   /// the internal SCEV records. For large loops, the former is faster.
   const bool ForgetSCEV;
 
+  /// If true, consider calls as inline candidates and defer unrolling so that
+  /// LTO post-link inlining can consider them first.
+  const bool PrepareForLTO;
+
 public:
   explicit LoopFullUnrollPass(int OptLevel = 2, bool OnlyWhenForced = false,
-                              bool ForgetSCEV = false)
+                              bool ForgetSCEV = false,
+                              bool PrepareForLTO = false)
       : OptLevel(OptLevel), OnlyWhenForced(OnlyWhenForced),
-        ForgetSCEV(ForgetSCEV) {}
+        ForgetSCEV(ForgetSCEV), PrepareForLTO(PrepareForLTO) {}
 
   PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM,
                         LoopStandardAnalysisResults &AR, LPMUpdater &U);
@@ -77,6 +82,10 @@ struct LoopUnrollOptions {
   /// the internal SCEV records. For large loops, the former is faster.
   const bool ForgetSCEV;
 
+  /// If true, consider calls as inline candidates and defer unrolling so that
+  /// LTO post-link inlining can consider them first.
+  bool PrepareForLTO = false;
+
   LoopUnrollOptions(int OptLevel = 2, bool OnlyWhenForced = false,
                     bool ForgetSCEV = false)
       : OptLevel(OptLevel), OnlyWhenForced(OnlyWhenForced),
@@ -125,6 +134,11 @@ struct LoopUnrollOptions {
     FullUnrollMaxCount = O;
     return *this;
   }
+
+  LoopUnrollOptions &setPrepareForLTO(bool V) {
+    PrepareForLTO = V;
+    return *this;
+  }
 };
 
 /// Loop unroll pass that will support both full and partial unrolling.
diff --git a/llvm/include/llvm/Transforms/Utils/UnrollLoop.h b/llvm/include/llvm/Transforms/Utils/UnrollLoop.h
index 364908ca1fad5..94f87b5ab652d 100644
--- a/llvm/include/llvm/Transforms/Utils/UnrollLoop.h
+++ b/llvm/include/llvm/Transforms/Utils/UnrollLoop.h
@@ -159,7 +159,7 @@ class UnrollCostEstimator {
 
   LLVM_ABI UnrollCostEstimator(const Loop *L, const TargetTransformInfo &TTI,
                                const SmallPtrSetImpl<const Value *> &EphValues,
-                               unsigned BEInsns);
+                               unsigned BEInsns, bool PrepareForLTO = false);
 
   /// Whether it is legal to unroll this loop.
   LLVM_ABI bool canUnroll() const;
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 2751961e684d2..11742713add1e 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -850,6 +850,8 @@ Expected<LoopUnrollOptions> parseLoopUnrollOptions(StringRef Params) {
       UnrollOpts.setRuntime(Enable);
     } else if (ParamName == "upperbound") {
       UnrollOpts.setUpperBound(Enable);
+    } else if (ParamName == "prepare-for-lto") {
+      UnrollOpts.setPrepareForLTO(Enable);
     } else {
       return make_error<StringError>(
           formatv("invalid LoopUnrollPass parameter '{}'", ParamName).str(),
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 9f7b3cf4df77b..06c5c43fc67e9 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -630,7 +630,7 @@ FUNCTION_PASS_WITH_PARAMS(
     parseLoopUnrollOptions,
     "O0;O1;O2;O3;full-unroll-max=N;no-partial;partial;no-peeling;peeling;"
     "no-profile-peeling;profile-peeling;no-runtime;runtime;no-upperbound;"
-    "upperbound")
+    "upperbound;prepare-for-lto")
 FUNCTION_PASS_WITH_PARAMS(
     "loop-vectorize", "LoopVectorizePass",
     [](LoopVectorizeOptions Opts) { return LoopVectorizePass(Opts); },
diff --git a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
index 885ae1f6dc02a..1b4bbabe451b5 100644
--- a/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp
@@ -687,11 +687,11 @@ static std::optional<EstimatedUnrollCost> analyzeLoopUnrollCost(
 
 UnrollCostEstimator::UnrollCostEstimator(
     const Loop *L, const TargetTransformInfo &TTI,
-    const SmallPtrSetImpl<const Value *> &EphValues, unsigned BEInsns) {
+    const SmallPtrSetImpl<const Value *> &EphValues, unsigned BEInsns,
+    bool PrepareForLTO) {
   CodeMetrics Metrics;
   for (BasicBlock *BB : L->blocks())
-    Metrics.analyzeBasicBlock(BB, TTI, EphValues, /* PrepareForLTO= */ false,
-                              L);
+    Metrics.analyzeBasicBlock(BB, TTI, EphValues, PrepareForLTO, L);
   NumInlineCandidates = Metrics.NumInlineCandidates;
   NotDuplicatable = Metrics.notDuplicatable;
   Convergence = Metrics.Convergence;
@@ -1245,7 +1245,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
                 OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
                 ProfileSummaryInfo *PSI, bool PreserveLCSSA, int OptLevel,
                 bool OnlyFullUnroll, bool OnlyWhenForced, bool ForgetAllSCEV,
-                std::optional<unsigned> ProvidedCount,
+                bool PrepareForLTO, std::optional<unsigned> ProvidedCount,
                 std::optional<unsigned> ProvidedThreshold,
                 std::optional<bool> ProvidedAllowPartial,
                 std::optional<bool> ProvidedRuntime,
@@ -1324,7 +1324,7 @@ tryToUnrollLoop(Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
   SmallPtrSet<const Value *, 32> EphValues;
   CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
 
-  UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns);
+  UnrollCostEstimator UCE(L, TTI, EphValues, UP.BEInsns, PrepareForLTO);
   if (!UCE.canUnroll())
     return LoopUnrollResult::Unmodified;
 
@@ -1553,10 +1553,11 @@ class LoopUnroll : public LoopPass {
 
     LoopUnrollResult Result = tryToUnrollLoop(
         L, DT, LI, SE, TTI, AC, ORE, nullptr, nullptr, PreserveLCSSA, OptLevel,
-        /*OnlyFullUnroll*/ false, OnlyWhenForced, ForgetAllSCEV, ProvidedCount,
-        ProvidedThreshold, ProvidedAllowPartial, ProvidedRuntime,
-        ProvidedUpperBound, ProvidedAllowPeeling,
-        ProvidedAllowProfileBasedPeeling, ProvidedFullUnrollMaxCount);
+        /*OnlyFullUnroll*/ false, OnlyWhenForced, ForgetAllSCEV,
+        /*PrepareForLTO*/ false, ProvidedCount, ProvidedThreshold,
+        ProvidedAllowPartial, ProvidedRuntime, ProvidedUpperBound,
+        ProvidedAllowPeeling, ProvidedAllowProfileBasedPeeling,
+        ProvidedFullUnrollMaxCount);
 
     if (Result == LoopUnrollResult::FullyUnrolled)
       LPM.markLoopAsDeleted(*L);
@@ -1625,7 +1626,8 @@ PreservedAnalyses LoopFullUnrollPass::run(Loop &L, LoopAnalysisManager &AM,
       tryToUnrollLoop(&L, AR.DT, &AR.LI, AR.SE, AR.TTI, AR.AC, ORE,
                       /*BFI*/ nullptr, /*PSI*/ nullptr,
                       /*PreserveLCSSA*/ true, OptLevel, /*OnlyFullUnroll*/ true,
-                      OnlyWhenForced, ForgetSCEV, /*Count*/ std::nullopt,
+                      OnlyWhenForced, ForgetSCEV, PrepareForLTO,
+                      /*Count*/ std::nullopt,
                       /*Threshold*/ std::nullopt, /*AllowPartial*/ false,
                       /*Runtime*/ false, /*UpperBound*/ false,
                       /*AllowPeeling*/ true,
@@ -1753,7 +1755,7 @@ PreservedAnalyses LoopUnrollPass::run(Function &F,
         &L, DT, &LI, SE, TTI, AC, ORE, BFI, PSI,
         /*PreserveLCSSA*/ true, UnrollOpts.OptLevel, /*OnlyFullUnroll*/ false,
         UnrollOpts.OnlyWhenForced, UnrollOpts.ForgetSCEV,
-        /*Count*/ std::nullopt,
+        UnrollOpts.PrepareForLTO, /*Count*/ std::nullopt,
         /*Threshold*/ std::nullopt, UnrollOpts.AllowPartial,
         UnrollOpts.AllowRuntime, UnrollOpts.AllowUpperBound, LocalAllowPeeling,
         UnrollOpts.AllowProfileBasedPeeling, UnrollOpts.FullUnrollMaxCount,
@@ -1795,6 +1797,8 @@ void LoopUnrollPass::printPipeline(
        << "profile-peeling;";
   if (UnrollOpts.FullUnrollMaxCount != std::nullopt)
     OS << "full-unroll-max=" << UnrollOpts.FullUnrollMaxCount << ';';
+  if (UnrollOpts.PrepareForLTO)
+    OS << "prepare-for-lto;";
   OS << 'O' << UnrollOpts.OptLevel;
   OS << '>';
 }
diff --git a/llvm/test/Other/new-pm-print-pipeline.ll b/llvm/test/Other/new-pm-print-pipeline.ll
index 53a17975c512b..6529aaa646297 100644
--- a/llvm/test/Other/new-pm-print-pipeline.ll
+++ b/llvm/test/Other/new-pm-print-pipeline.ll
@@ -28,8 +28,8 @@
 ; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(lower-matrix-intrinsics<>,lower-matrix-intrinsics<minimal>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-9
 ; CHECK-9: function(lower-matrix-intrinsics<>,lower-matrix-intrinsics<minimal>)
 
-; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(loop-unroll<>,loop-unroll<partial;peeling;runtime;upperbound;profile-peeling;full-unroll-max=5;O1>,loop-unroll<no-partial;no-peeling;no-runtime;no-upperbound;no-profile-peeling;full-unroll-max=7;O1>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-10
-; CHECK-10: function(loop-unroll<O2>,loop-unroll<partial;peeling;runtime;upperbound;profile-peeling;full-unroll-max=5;O1>,loop-unroll<no-partial;no-peeling;no-runtime;no-upperbound;no-profile-peeling;full-unroll-max=7;O1>)
+; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(loop-unroll<>,loop-unroll<partial;peeling;runtime;upperbound;profile-peeling;full-unroll-max=5;O1>,loop-unroll<no-partial;no-peeling;no-runtime;no-upperbound;no-profile-peeling;full-unroll-max=7;O1>,loop-unroll<prepare-for-lto>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-10
+; CHECK-10: function(loop-unroll<O2>,loop-unroll<partial;peeling;runtime;upperbound;profile-peeling;full-unroll-max=5;O1>,loop-unroll<no-partial;no-peeling;no-runtime;no-upperbound;no-profile-peeling;full-unroll-max=7;O1>,loop-unroll<prepare-for-lto;O2>)
 
 ; RUN: opt -disable-output -disable-verify -print-pipeline-passes -passes='function(gvn<>,gvn<pre;load-pre;split-backedge-load-pre;memdep;memoryssa>,gvn<no-pre;no-load-pre;no-split-backedge-load-pre;no-memdep;no-memoryssa>)' < %s | FileCheck %s --match-full-lines --check-prefixes=CHECK-11
 ; CHECK-11: function(gvn<>,gvn<pre;load-pre;split-backedge-load-pre;no-memdep;memoryssa>,gvn<no-pre;no-load-pre;no-split-backedge-load-pre;memdep;no-memoryssa>)
diff --git a/llvm/test/Other/print-passes.ll b/llvm/test/Other/print-passes.ll
index 955a0cd225468..80b1c3de535fd 100644
--- a/llvm/test/Other/print-passes.ll
+++ b/llvm/test/Other/print-passes.ll
@@ -13,7 +13,7 @@
 ; CHECK: Function passes:
 ; CHECK: no-op-function
 ; CHECK: Function passes with params:
-; CHECK: loop-unroll<O0;O1;O2;O3;full-unroll-max=N;no-partial;partial;no-peeling;peeling;no-profile-peeling;profile-peeling;no-runtime;runtime;no-upperbound;upperbound>
+; CHECK: loop-unroll<O0;O1;O2;O3;full-unroll-max=N;no-partial;partial;no-peeling;peeling;no-profile-peeling;profile-peeling;no-runtime;runtime;no-upperbound;upperbound;prepare-for-lto>
 ; CHECK: Function analyses:
 ; CHECK: no-op-function
 ; CHECK: Function alias analyses:
diff --git a/llvm/test/Transforms/LoopUnroll/prepare-for-lto.ll b/llvm/test/Transforms/LoopUnroll/prepare-for-lto.ll
new file mode 100644
index 0000000000000..3adf2e715a3ac
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/prepare-for-lto.ll
@@ -0,0 +1,165 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes='loop-unroll' < %s | FileCheck --check-prefix=DEFAULT %s
+; RUN: opt -S -passes='loop-unroll<prepare-for-lto>' < %s | FileCheck --check-prefix=LTO %s
+
+; a) Loop with extern declaration call — normally fully unrolled, but deferred
+;    under prepare-for-lto because the call may be inlined during LTO.
+declare i32 @extern_func(i32)
+
+define i32 @extern_call(ptr %A) {
+; DEFAULT-LABEL: @extern_call(
+; DEFAULT-NEXT:  entry:
+; DEFAULT-NEXT:    br label [[LOOP:%.*]]
+; DEFAULT:       loop:
+; DEFAULT-NEXT:    [[V:%.*]] = call i32 @extern_func(i32 0)
+; DEFAULT-NEXT:    [[V_1:%.*]] = call i32 @extern_func(i32 1)
+; DEFAULT-NEXT:    [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
+; DEFAULT-NEXT:    [[V_2:%.*]] = call i32 @extern_func(i32 2)
+; DEFAULT-NEXT:    [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
+; DEFAULT-NEXT:    [[V_3:%.*]] = call i32 @extern_func(i32 3)
+; DEFAULT-NEXT:    [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
+; DEFAULT-NEXT:    ret i32 [[ADD_3]]
+;
+; LTO-LABEL: @extern_call(
+; LTO-NEXT:  entry:
+; LTO-NEXT:    br label [[LOOP:%.*]]
+; LTO:       loop:
+; LTO-NEXT:    [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; LTO-NEXT:    [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
+; LTO-NEXT:    [[V:%.*]] = call i32 @extern_func(i32 [[I]])
+; LTO-NEXT:    [[ADD]] = add i32 [[SUM]], [[V]]
+; LTO-NEXT:    [[INC]] = add i32 [[I]], 1
+; LTO-NEXT:    [[CMP:%.*]] = icmp ult i32 [[INC]], 4
+; LTO-NEXT:    br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
+; LTO:       exit:
+; LTO-NEXT:    [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
+; LTO-NEXT:    ret i32 [[ADD_LCSSA]]
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %inc, %loop ]
+  %sum = phi i32 [ 0, %entry ], [ %add, %loop ]
+  %v = call i32 @extern_func(i32 %i)
+  %add = add i32 %sum, %v
+  %inc = add i32 %i, 1
+  %cmp = icmp ult i32 %inc, 4
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret i32 %add
+}
+
+; b) Loop with internal single-use call — suppressed in both modes because
+;    internal single-use functions are always considered inline candidates.
+define internal i32 @helper(i32 %x) {
+; DEFAULT-LABEL: @helper(
+; DEFAULT-NEXT:    [[ADD:%.*]] = add i32 [[X:%.*]], 42
+; DEFAULT-NEXT:    ret i32 [[ADD]]
+;
+; LTO-LABEL: @helper(
+; LTO-NEXT:    [[ADD:%.*]] = add i32 [[X:%.*]], 42
+; LTO-NEXT:    ret i32 [[ADD]]
+;
+  %add = add i32 %x, 42
+  ret i32 %add
+}
+
+define i32 @internal_single_use_call(ptr %A) {
+; DEFAULT-LABEL: @internal_single_use_call(
+; DEFAULT-NEXT:  entry:
+; DEFAULT-NEXT:    br label [[LOOP:%.*]]
+; DEFAULT:       loop:
+; DEFAULT-NEXT:    [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; DEFAULT-NEXT:    [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
+; DEFAULT-NEXT:    [[V:%.*]] = call i32 @helper(i32 [[I]])
+; DEFAULT-NEXT:    [[ADD]] = add i32 [[SUM]], [[V]]
+; DEFAULT-NEXT:    [[INC]] = add i32 [[I]], 1
+; DEFAULT-NEXT:    [[CMP:%.*]] = icmp ult i32 [[INC]], 4
+; DEFAULT-NEXT:    br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
+; DEFAULT:       exit:
+; DEFAULT-NEXT:    [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
+; DEFAULT-NEXT:    ret i32 [[ADD_LCSSA]]
+;
+; LTO-LABEL: @internal_single_use_call(
+; LTO-NEXT:  entry:
+; LTO-NEXT:    br label [[LOOP:%.*]]
+; LTO:       loop:
+; LTO-NEXT:    [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[INC:%.*]], [[LOOP]] ]
+; LTO-NEXT:    [[SUM:%.*]] = phi i32 [ 0, [[ENTRY]] ], [ [[ADD:%.*]], [[LOOP]] ]
+; LTO-NEXT:    [[V:%.*]] = call i32 @helper(i32 [[I]])
+; LTO-NEXT:    [[ADD]] = add i32 [[SUM]], [[V]]
+; LTO-NEXT:    [[INC]] = add i32 [[I]], 1
+; LTO-NEXT:    [[CMP:%.*]] = icmp ult i32 [[INC]], 4
+; LTO-NEXT:    br i1 [[CMP]], label [[LOOP]], label [[EXIT:%.*]]
+; LTO:       exit:
+; LTO-NEXT:    [[ADD_LCSSA:%.*]] = phi i32 [ [[ADD]], [[LOOP]] ]
+; LTO-NEXT:    ret i32 [[ADD_LCSSA]]
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %inc, %loop ]
+  %sum = phi i32 [ 0, %entry ], [ %add, %loop ]
+  %v = call i32 @helper(i32 %i)
+  %add = add i32 %sum, %v
+  %inc = add i32 %i, 1
+  %cmp = icmp ult i32 %inc, 4
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret i32 %add
+}
+
+; c) Loop without calls — fully unrolled in both modes.
+define i32 @no_call(ptr %A) {
+; DEFAULT-LABEL: @no_call(
+; DEFAULT-NEXT:  entry:
+; DEFAULT-NEXT:    br label [[LOOP:%.*]]
+; DEFAULT:       loop:
+; DEFAULT-NEXT:    [[V:%.*]] = load i32, ptr [[A:%.*]], align 4
+; DEFAULT-NEXT:    [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 1
+; DEFAULT-NEXT:    [[V_1:%.*]] = load i32, ptr [[ARRAYIDX_1]], align 4
+; DEFAULT-NEXT:    [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
+; DEFAULT-NEXT:    [[ARRAYIDX_2:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 2
+; DEFAULT-NEXT:    [[V_2:%.*]] = load i32, ptr [[ARRAYIDX_2]], align 4
+; DEFAULT-NEXT:    [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
+; DEFAULT-NEXT:    [[ARRAYIDX_3:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 3
+; DEFAULT-NEXT:    [[V_3:%.*]] = load i32, ptr [[ARRAYIDX_3]], align 4
+; DEFAULT-NEXT:    [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
+; DEFAULT-NEXT:    ret i32 [[ADD_3]]
+;
+; LTO-LABEL: @no_call(
+; LTO-NEXT:  entry:
+; LTO-NEXT:    br label [[LOOP:%.*]]
+; LTO:       loop:
+; LTO-NEXT:    [[V:%.*]] = load i32, ptr [[A:%.*]], align 4
+; LTO-NEXT:    [[ARRAYIDX_1:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 1
+; LTO-NEXT:    [[V_1:%.*]] = load i32, ptr [[ARRAYIDX_1]], align 4
+; LTO-NEXT:    [[ADD_1:%.*]] = add i32 [[V]], [[V_1]]
+; LTO-NEXT:    [[ARRAYIDX_2:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 2
+; LTO-NEXT:    [[V_2:%.*]] = load i32, ptr [[ARRAYIDX_2]], align 4
+; LTO-NEXT:    [[ADD_2:%.*]] = add i32 [[ADD_1]], [[V_2]]
+; LTO-NEXT:    [[ARRAYIDX_3:%.*]] = getelementptr inbounds i32, ptr [[A]], i32 3
+; LTO-NEXT:    [[V_3:%.*]] = load i32, ptr [[ARRAYIDX_3]], align 4
+; LTO-NEXT:    [[ADD_3:%.*]] = add i32 [[ADD_2]], [[V_3]]
+; LTO-NEXT:    ret i32 [[ADD_3]]
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %inc, %loop ]
+  %sum = phi i32 [ 0, %entry ], [ %add, %loop ]
+  %arrayidx = getelementptr inbounds i32, ptr %A, i32 %i
+  %v = load i32, ptr %arrayidx
+  %add = add i32 %sum, %v
+  %inc = add i32 %i, 1
+  %cmp = icmp ult i32 %inc, 4
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret i32 %add
+}

``````````

</details>


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


More information about the llvm-commits mailing list