[llvm] [lsr][term-fold] Restrict expansion budget, particularly for profiled… (PR #74747)

Philip Reames via llvm-commits llvm-commits at lists.llvm.org
Thu Dec 7 10:55:17 PST 2023


https://github.com/preames created https://github.com/llvm/llvm-project/pull/74747

… loops

This is a follow up to an item I noted in my submission comment for e947f95.  I don't have a real world example where this is triggering unprofitably, but avoiding the transform when we estimate the loop to be short running from profiling seems quite reasonable.

A couple of observations:
* Because we already checked for affine addrecs, the worst expansion we're going to see is the unknown trip count * unknown step case (both are loop invariant).  I picked a fallback cost (2 * SCEV's default expansion budget of 4) which was high enough to allow this.  These cases will end up being somewhat rare anyways as finding one where we can prove legality takes some work.
* Short running loops with constant trip counts produce either constant expressions (if the step is constant) or multiplications by small constant (if the step is unknown).  Both of these are cheap by any reasonable budget and thus not wroth checking for explicitly.
* Estimated trip counts come from profile data, loops without profile data will not have one.

Overall, this doesn't end up making much of a difference in practical behavior of the transform.  Probably still worth landing, but I expect this to be pretty low impact.

Possible future work (which I have no plans of doing at this time):
* Ppick the lowest cost alternate IV if we have multiple ones under the budget.
* Use SCEV to prove a constant upper bound on the trip count, and restrict the transform for unknown-but-small trip counts.
* I think we can now remove the affine restriction if we wanted.  Would need to think carefully about legality on this one.

I don't have examples where any of these would be profitable in practice, but we'll see what comes up in the future.

>From 0aad007ef1372d7b7bd74aae10261b96bfae07e0 Mon Sep 17 00:00:00 2001
From: Philip Reames <preames at rivosinc.com>
Date: Thu, 7 Dec 2023 10:40:46 -0800
Subject: [PATCH] [lsr][term-fold] Restrict expansion budget, particularly for
 profiled loops

This is a follow up to an item I noted in my submission comment for
e947f95.  I don't have a real world example where this is triggering
unprofitably, but avoiding the transform when we estimate the loop
to be short running from profiling seems quite reasonable.

A couple of observations:
* Because we already checked for affine addrecs, the worst expansion we're
  going to see is the unknown trip count * unknown step case (both are
  loop invariant).  I picked a fallback cost (2 * SCEV's default expansion
  budget of 4) which was high enough to allow this.  These cases will
  end up being somewhat rare anyways as finding one where we can prove
  legality takes some work.
* Short running loops with constant trip counts produce either constant
  expressions (if the step is constant) or multiplications by small
  constant (if the step is unknown).  Both of these are cheap by any
  reasonable budget and thus not wroth checking for explicitly.
* Estimated trip counts come from profile data, loops without profile
  data will not have one.

Overall, this doesn't end up making much of a difference in practical
behavior of the transform.  Probably still worth landing, but I expect
this to be pretty low impact.

Possible future work (which I have no plans of doing at this time):
* Ppick the lowest cost alternate IV if we have multiple ones under the
  budget.
* Use SCEV to prove a constant upper bound on the trip count, and restrict
  the transform for unknown-but-small trip counts.
* I think we can now remove the affine restriction if we wanted.  Would
  need to think carefully about legality on this one.

I don't have examples where any of these would be profitable in practice,
but we'll see what comes up in the future.
---
 .../Transforms/Scalar/LoopStrengthReduce.cpp  | 22 +++++++++++++++++--
 .../LoopStrengthReduce/lsr-term-fold.ll       | 15 +++++--------
 2 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index 39607464dd0090..2bcf6a2d659c08 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -6730,7 +6730,7 @@ static llvm::PHINode *GetInductionVariable(const Loop &L, ScalarEvolution &SE,
 
 static std::optional<std::tuple<PHINode *, PHINode *, const SCEV *, bool>>
 canFoldTermCondOfLoop(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
-                      const LoopInfo &LI) {
+                      const LoopInfo &LI, const TargetTransformInfo &TTI) {
   if (!L->isInnermost()) {
     LLVM_DEBUG(dbgs() << "Cannot fold on non-innermost loop\n");
     return std::nullopt;
@@ -6781,6 +6781,15 @@ canFoldTermCondOfLoop(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
   if (!isAlmostDeadIV(ToFold, LoopLatch, TermCond))
     return std::nullopt;
 
+  // Inserting instructions in the preheader has a runtime cost, scale
+  // the allowed cost with the loops trip count as best we can.
+  const unsigned ExpansionBudget = [&]() {
+    if (std::optional<unsigned> SmallTC = getLoopEstimatedTripCount(L))
+      return std::min(2*SCEVCheapExpansionBudget, *SmallTC);
+    // Unknown trip count, assume long running by default.
+    return 2*SCEVCheapExpansionBudget;
+  }();
+
   const SCEV *BECount = SE.getBackedgeTakenCount(L);
   const DataLayout &DL = L->getHeader()->getModule()->getDataLayout();
   SCEVExpander Expander(SE, DL, "lsr_fold_term_cond");
@@ -6788,6 +6797,7 @@ canFoldTermCondOfLoop(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
   PHINode *ToHelpFold = nullptr;
   const SCEV *TermValueS = nullptr;
   bool MustDropPoison = false;
+  auto InsertPt = L->getLoopPreheader()->getTerminator();
   for (PHINode &PN : L->getHeader()->phis()) {
     if (ToFold == &PN)
       continue;
@@ -6828,6 +6838,14 @@ canFoldTermCondOfLoop(Loop *L, ScalarEvolution &SE, DominatorTree &DT,
       continue;
     }
 
+    if (Expander.isHighCostExpansion(TermValueSLocal, L, ExpansionBudget,
+                                     &TTI, InsertPt)) {
+      LLVM_DEBUG(
+          dbgs() << "Is too expensive to expand terminating value for phi node"
+                 << PN << "\n");
+      continue;
+    }
+
     // The candidate IV may have been otherwise dead and poison from the
     // very first iteration.  If we can't disprove that, we can't use the IV.
     if (!mustExecuteUBIfPoisonOnPathTo(&PN, LoopLatch->getTerminator(), &DT)) {
@@ -6953,7 +6971,7 @@ static bool ReduceLoopStrength(Loop *L, IVUsers &IU, ScalarEvolution &SE,
   }();
 
   if (EnableFormTerm) {
-    if (auto Opt = canFoldTermCondOfLoop(L, SE, DT, LI)) {
+    if (auto Opt = canFoldTermCondOfLoop(L, SE, DT, LI, TTI)) {
       auto [ToFold, ToHelpFold, TermValueS, MustDrop] = *Opt;
 
       Changed = true;
diff --git a/llvm/test/Transforms/LoopStrengthReduce/lsr-term-fold.ll b/llvm/test/Transforms/LoopStrengthReduce/lsr-term-fold.ll
index 0203abe69ac299..5086f533c38d81 100644
--- a/llvm/test/Transforms/LoopStrengthReduce/lsr-term-fold.ll
+++ b/llvm/test/Transforms/LoopStrengthReduce/lsr-term-fold.ll
@@ -478,20 +478,15 @@ define void @expensive_expand_short_tc(ptr %a, i32 %offset, i32 %n) {
 ; CHECK-LABEL: @expensive_expand_short_tc(
 ; CHECK-NEXT:  entry:
 ; CHECK-NEXT:    [[UGLYGEP:%.*]] = getelementptr i8, ptr [[A:%.*]], i64 84
-; CHECK-NEXT:    [[TMP0:%.*]] = add i32 [[N:%.*]], -1
-; CHECK-NEXT:    [[TMP1:%.*]] = zext i32 [[TMP0]] to i64
-; CHECK-NEXT:    [[TMP2:%.*]] = add nuw nsw i64 [[TMP1]], 1
-; CHECK-NEXT:    [[TMP3:%.*]] = sext i32 [[OFFSET:%.*]] to i64
-; CHECK-NEXT:    [[TMP4:%.*]] = mul i64 [[TMP2]], [[TMP3]]
-; CHECK-NEXT:    [[TMP5:%.*]] = add nsw i64 [[TMP4]], 84
-; CHECK-NEXT:    [[SCEVGEP:%.*]] = getelementptr i8, ptr [[A]], i64 [[TMP5]]
 ; CHECK-NEXT:    br label [[FOR_BODY:%.*]]
 ; CHECK:       for.body:
 ; CHECK-NEXT:    [[LSR_IV1:%.*]] = phi ptr [ [[UGLYGEP2:%.*]], [[FOR_BODY]] ], [ [[UGLYGEP]], [[ENTRY:%.*]] ]
+; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i32 [ [[LSR_IV_NEXT:%.*]], [[FOR_BODY]] ], [ 0, [[ENTRY]] ]
 ; CHECK-NEXT:    store i32 1, ptr [[LSR_IV1]], align 4
-; CHECK-NEXT:    [[UGLYGEP2]] = getelementptr i8, ptr [[LSR_IV1]], i32 [[OFFSET]]
-; CHECK-NEXT:    [[LSR_FOLD_TERM_COND_REPLACED_TERM_COND:%.*]] = icmp eq ptr [[UGLYGEP2]], [[SCEVGEP]]
-; CHECK-NEXT:    br i1 [[LSR_FOLD_TERM_COND_REPLACED_TERM_COND]], label [[FOR_END:%.*]], label [[FOR_BODY]], !prof [[PROF0:![0-9]+]]
+; CHECK-NEXT:    [[LSR_IV_NEXT]] = add nsw i32 [[LSR_IV]], 1
+; CHECK-NEXT:    [[UGLYGEP2]] = getelementptr i8, ptr [[LSR_IV1]], i32 [[OFFSET:%.*]]
+; CHECK-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i32 [[LSR_IV_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[EXITCOND_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]], !prof [[PROF0:![0-9]+]]
 ; CHECK:       for.end:
 ; CHECK-NEXT:    ret void
 ;



More information about the llvm-commits mailing list