[llvm] [LoopUnroll] Add `prepare-for-lto` pass option (PR #192153)
Justin Fargnoli via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 14 16:56:23 PDT 2026
https://github.com/justinfargnoli updated https://github.com/llvm/llvm-project/pull/192153
>From cb0dfe9c800668310ca8df0889f0ab8626095b91 Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Tue, 14 Apr 2026 22:51:00 +0000
Subject: [PATCH] [LoopUnroll] Add prepare-for-lto option to LoopUnrollPass
Expose the existing PrepareForLTO mechanism through the LoopUnrollPass
and LoopFullUnrollPass options, allowing it to be controlled via the
pass pipeline string (e.g. loop-unroll<prepare-for-lto>).
When enabled, the loop unroller considers external function calls as
potential inline candidates, deferring unrolling of loops that contain
such calls. This allows LTO to inline these calls first, potentially
enabling better unrolling decisions in the post-link phase.
The underlying infrastructure already existed in CodeMetrics and was
used by LoopRotatePass; this patch wires it up for LoopUnrollPass as
well.
---
.../llvm/Transforms/Scalar/LoopUnrollPass.h | 18 +-
.../llvm/Transforms/Utils/UnrollLoop.h | 2 +-
llvm/lib/Passes/PassBuilder.cpp | 2 +
llvm/lib/Passes/PassRegistry.def | 2 +-
llvm/lib/Transforms/Scalar/LoopUnrollPass.cpp | 26 +--
llvm/test/Other/new-pm-print-pipeline.ll | 4 +-
.../Transforms/LoopUnroll/prepare-for-lto.ll | 165 ++++++++++++++++++
7 files changed, 202 insertions(+), 17 deletions(-)
create mode 100644 llvm/test/Transforms/LoopUnroll/prepare-for-lto.ll
diff --git a/llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h b/llvm/include/llvm/Transforms/Scalar/LoopUnrollPass.h
index 8d8c2f254f02f..7f3ae779569f1 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 to allow
+ /// inlining to resolve them first (e.g. during LTO pre-link).
+ 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 to allow
+ /// inlining to resolve them first (e.g. during LTO pre-link).
+ 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/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
+}
More information about the llvm-commits
mailing list