[llvm] [LICM] Make an integer version of hoistFPAssociation. (PR #67736)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 12 09:41:27 PST 2024


https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/67736

>From 53416bafffd98e399e8f99a19c64f68fe7e82d0f Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 28 Sep 2023 13:51:06 -0700
Subject: [PATCH 1/4] [LICM][WIP] Make an integer version of
 hoistFPAssociation.

This is a naive copy/paste of the FP code. We could do more
integration to share code.

Posting for discussion.
---
 llvm/lib/Transforms/Scalar/LICM.cpp           |   74 ++
 .../Transforms/LICM/expr-reassociate-int.ll   | 1078 +++++++++++++++++
 2 files changed, 1152 insertions(+)
 create mode 100644 llvm/test/Transforms/LICM/expr-reassociate-int.ll

diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index f3e40a5cb809bc..3e534aa20568e5 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -110,6 +110,9 @@ STATISTIC(NumAddSubHoisted, "Number of add/subtract expressions reassociated "
                             "and hoisted out of the loop");
 STATISTIC(NumFPAssociationsHoisted, "Number of invariant FP expressions "
                                     "reassociated and hoisted out of the loop");
+STATISTIC(NumIntAssociationsHoisted,
+          "Number of invariant int expressions "
+          "reassociated and hoisted out of the loop");
 
 /// Memory promotion is enabled by default.
 static cl::opt<bool>
@@ -135,6 +138,12 @@ static cl::opt<unsigned> FPAssociationUpperLimit(
         "Set upper limit for the number of transformations performed "
         "during a single round of hoisting the reassociated expressions."));
 
+cl::opt<unsigned> IntAssociationUpperLimit(
+    "licm-max-num-int-reassociations", cl::init(5U), cl::Hidden,
+    cl::desc(
+        "Set upper limit for the number of transformations performed "
+        "during a single round of hoisting the reassociated expressions."));
+
 // Experimental option to allow imprecision in LICM in pathological cases, in
 // exchange for faster compile. This is to be removed if MemorySSA starts to
 // address the same issue. LICM calls MemorySSAWalker's
@@ -2727,6 +2736,65 @@ static bool hoistFPAssociation(Instruction &I, Loop &L,
   return true;
 }
 
+static bool hoistIntAssociation(Instruction &I, Loop &L,
+                                ICFLoopSafetyInfo &SafetyInfo,
+                                MemorySSAUpdater &MSSAU, AssumptionCache *AC,
+                                DominatorTree *DT) {
+  using namespace PatternMatch;
+  Value *VariantOp = nullptr, *InvariantOp = nullptr;
+
+  if (!match(&I, m_Mul(m_Value(VariantOp), m_Value(InvariantOp))))
+    return false;
+  if (L.isLoopInvariant(VariantOp))
+    std::swap(VariantOp, InvariantOp);
+  if (L.isLoopInvariant(VariantOp) || !L.isLoopInvariant(InvariantOp))
+    return false;
+  Value *Factor = InvariantOp;
+
+  // First, we need to make sure we should do the transformation.
+  SmallVector<Use *> Changes;
+  SmallVector<BinaryOperator *> Worklist;
+  if (BinaryOperator *VariantBinOp = dyn_cast<BinaryOperator>(VariantOp))
+    Worklist.push_back(VariantBinOp);
+  while (!Worklist.empty()) {
+    BinaryOperator *BO = Worklist.pop_back_val();
+    if (!BO->hasOneUse())
+      return false;
+    BinaryOperator *Op0, *Op1;
+    if (match(BO, m_Add(m_BinOp(Op0), m_BinOp(Op1)))) {
+      Worklist.push_back(Op0);
+      Worklist.push_back(Op1);
+      continue;
+    }
+    if (BO->getOpcode() != Instruction::Mul || L.isLoopInvariant(BO))
+      return false;
+    Use &U0 = BO->getOperandUse(0);
+    Use &U1 = BO->getOperandUse(1);
+    if (L.isLoopInvariant(U0))
+      Changes.push_back(&U0);
+    else if (L.isLoopInvariant(U1))
+      Changes.push_back(&U1);
+    else
+      return false;
+    if (Changes.size() > IntAssociationUpperLimit)
+      return false;
+  }
+  if (Changes.empty())
+    return false;
+
+  // We know we should do it so let's do the transformation.
+  auto *Preheader = L.getLoopPreheader();
+  assert(Preheader && "Loop is not in simplify form?");
+  IRBuilder<> Builder(Preheader->getTerminator());
+  for (auto *U : Changes) {
+    assert(L.isLoopInvariant(U->get()));
+    U->set(Builder.CreateMul(U->get(), Factor, "factor.op.mul"));
+  }
+  I.replaceAllUsesWith(VariantOp);
+  eraseInstruction(I, SafetyInfo, MSSAU);
+  return true;
+}
+
 static bool hoistArithmetics(Instruction &I, Loop &L,
                              ICFLoopSafetyInfo &SafetyInfo,
                              MemorySSAUpdater &MSSAU, AssumptionCache *AC,
@@ -2760,6 +2828,12 @@ static bool hoistArithmetics(Instruction &I, Loop &L,
     return true;
   }
 
+  if (hoistIntAssociation(I, L, SafetyInfo, MSSAU, AC, DT)) {
+    ++NumHoisted;
+    ++NumIntAssociationsHoisted;
+    return true;
+  }
+
   return false;
 }
 
diff --git a/llvm/test/Transforms/LICM/expr-reassociate-int.ll b/llvm/test/Transforms/LICM/expr-reassociate-int.ll
new file mode 100644
index 00000000000000..556bcfa4dbfd0b
--- /dev/null
+++ b/llvm/test/Transforms/LICM/expr-reassociate-int.ll
@@ -0,0 +1,1078 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
+; RUN: opt -passes='reassociate' -S < %s | FileCheck %s --check-prefix=REASSOCIATE_ONLY
+; RUN: opt -passes='licm' -S < %s | FileCheck %s --check-prefix=LICM_ONLY
+; RUN: opt -passes='licm' -licm-max-num-int-reassociations=1 -S < %s | FileCheck %s --check-prefix=LICM_ONLY_CONSTRAINED
+; RUN: opt -passes='reassociate,loop-mssa(licm)' -S < %s | FileCheck %s --check-prefix=LICM_AFTER_REASSOCIATE
+; RUN: opt -passes='reassociate,loop-mssa(licm)' -licm-max-num-int-reassociations=1 -S < %s | FileCheck %s --check-prefix=LICM_AFTER_REASSOCIATE_CONSTRAINED
+
+;
+; A simple loop, should not get modified:
+;
+;  int j;
+;  const uint64_t d1d = d1 * delta;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = d1d * cells[j + 1];
+;
+
+define void @innermost_loop_1d(i32 %i, i64 %d1, i64 %delta, ptr %cells) {
+; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_1d
+; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; REASSOCIATE_ONLY-NEXT:  entry:
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; REASSOCIATE_ONLY:       for.cond:
+; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; REASSOCIATE_ONLY:       for.body:
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; REASSOCIATE_ONLY-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
+; REASSOCIATE_ONLY:       for.end:
+; REASSOCIATE_ONLY-NEXT:    ret void
+;
+; LICM_ONLY-LABEL: define void @innermost_loop_1d
+; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY-NEXT:  entry:
+; LICM_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY:       for.cond:
+; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY:       for.body:
+; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY:       for.end:
+; LICM_ONLY-NEXT:    ret void
+;
+; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_1d
+; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY_CONSTRAINED-NEXT:  entry:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY_CONSTRAINED:       for.cond:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY_CONSTRAINED:       for.body:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY_CONSTRAINED:       for.end:
+; LICM_ONLY_CONSTRAINED-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_1d
+; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE:       for.cond:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE:       for.body:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE:       for.end:
+; LICM_AFTER_REASSOCIATE-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_1d
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+;
+entry:
+  %fmul.d1 = mul i64 %d1, %delta
+  br label %for.cond
+
+for.cond:
+  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
+  %cmp.not = icmp sgt i32 %j, %i
+  br i1 %cmp.not, label %for.end, label %for.body
+
+for.body:
+  %add.j.1 = add nuw nsw i32 %j, 1
+  %idxprom.j.1 = zext i32 %add.j.1 to i64
+  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
+  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
+  %fmul.1 = mul i64 %fmul.d1, %cell.1
+  %idxprom.j = zext i32 %j to i64
+  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
+  store i64 %fmul.1, ptr %arrayidx.j, align 8
+  br label %for.cond
+
+for.end:
+  ret void
+}
+
+;
+; A simple loop:
+;
+;  int j;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = d1 * cells[j + 1] * delta;
+;
+; ...should be transformed by the LICM pass into this:
+;
+;  int j;
+;  const uint64_t d1d = d1 * delta;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = d1d * cells[j + 1];
+;
+
+define void @innermost_loop_1d_shouldhoist(i32 %i, i64 %d1, i64 %delta, ptr %cells) {
+; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_1d_shouldhoist
+; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; REASSOCIATE_ONLY-NEXT:  entry:
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; REASSOCIATE_ONLY:       for.cond:
+; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; REASSOCIATE_ONLY:       for.body:
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_1]], [[CELL_1]]
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; REASSOCIATE_ONLY-NEXT:    store i64 [[FMUL_2]], ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
+; REASSOCIATE_ONLY:       for.end:
+; REASSOCIATE_ONLY-NEXT:    ret void
+;
+; LICM_ONLY-LABEL: define void @innermost_loop_1d_shouldhoist
+; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY-NEXT:  entry:
+; LICM_ONLY-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY:       for.cond:
+; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY:       for.body:
+; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FACTOR_OP_MUL]], [[CELL_1]]
+; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY:       for.end:
+; LICM_ONLY-NEXT:    ret void
+;
+; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_1d_shouldhoist
+; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY_CONSTRAINED-NEXT:  entry:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY_CONSTRAINED:       for.cond:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY_CONSTRAINED:       for.body:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FACTOR_OP_MUL]], [[CELL_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY_CONSTRAINED:       for.end:
+; LICM_ONLY_CONSTRAINED-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_1d_shouldhoist
+; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE:       for.cond:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE:       for.body:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_1]], [[CELL_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[FMUL_2]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE:       for.end:
+; LICM_AFTER_REASSOCIATE-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_1d_shouldhoist
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_1]], [[CELL_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[FMUL_2]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+;
+entry:
+  br label %for.cond
+
+for.cond:
+  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
+  %cmp.not = icmp sgt i32 %j, %i
+  br i1 %cmp.not, label %for.end, label %for.body
+
+for.body:
+  %add.j.1 = add nuw nsw i32 %j, 1
+  %idxprom.j.1 = zext i32 %add.j.1 to i64
+  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
+  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
+  %fmul.1 = mul i64 %d1, %cell.1
+  %fmul.2 = mul i64 %fmul.1, %delta
+  %idxprom.j = zext i32 %j to i64
+  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
+  store i64 %fmul.2, ptr %arrayidx.j, align 8
+  br label %for.cond
+
+for.end:
+  ret void
+}
+
+;
+; The following loop will be modified by the 'Reassociate expressions' pass,
+;
+;  int j;
+;  const uint64_t d1d = d1 * delta;
+;  const uint64_t d2d = d2 * delta;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = d1d * cells[j + 1] + d2d * cells[j];
+;
+; ...into this:
+;
+;  int j;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = (d1 * cells[j + 1] + d2 * cells[j]) * delta;
+;
+; We expect the LICM pass to undo this transformation.
+;
+
+define void @innermost_loop_2d(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
+; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_2d
+; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; REASSOCIATE_ONLY-NEXT:  entry:
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; REASSOCIATE_ONLY:       for.cond:
+; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; REASSOCIATE_ONLY:       for.body:
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
+; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
+; REASSOCIATE_ONLY:       for.end:
+; REASSOCIATE_ONLY-NEXT:    ret void
+;
+; LICM_ONLY-LABEL: define void @innermost_loop_2d
+; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY-NEXT:  entry:
+; LICM_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY:       for.cond:
+; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY:       for.body:
+; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
+; LICM_ONLY-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
+; LICM_ONLY-NEXT:    store i64 [[FADD_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY:       for.end:
+; LICM_ONLY-NEXT:    ret void
+;
+; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_2d
+; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY_CONSTRAINED-NEXT:  entry:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY_CONSTRAINED:       for.cond:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY_CONSTRAINED:       for.body:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FADD_1]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY_CONSTRAINED:       for.end:
+; LICM_ONLY_CONSTRAINED-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_2d
+; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE:       for.cond:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE:       for.body:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE:       for.end:
+; LICM_AFTER_REASSOCIATE-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_2d
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+;
+entry:
+  %fmul.d1 = mul i64 %d1, %delta
+  %fmul.d2 = mul i64 %d2, %delta
+  br label %for.cond
+
+for.cond:
+  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
+  %cmp.not = icmp sgt i32 %j, %i
+  br i1 %cmp.not, label %for.end, label %for.body
+
+for.body:
+  %add.j.1 = add nuw nsw i32 %j, 1
+  %idxprom.j.1 = zext i32 %add.j.1 to i64
+  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
+  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
+  %fmul.1 = mul i64 %fmul.d1, %cell.1
+  %idxprom.j = zext i32 %j to i64
+  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
+  %cell.2 = load i64, ptr %arrayidx.j, align 8
+  %fmul.2 = mul i64 %fmul.d2, %cell.2
+  %fadd.1 = add i64 %fmul.1, %fmul.2
+  store i64 %fadd.1, ptr %arrayidx.j, align 8
+  br label %for.cond
+
+for.end:
+  ret void
+}
+
+;
+; The following loop will be modified by the 'Reassociate expressions' pass,
+;
+;  int j;
+;  const uint64_t d1d = d1 * delta;
+;  const uint64_t d2d = d2 * delta;
+;  const uint64_t d3d = d3 * delta;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = d1d * cells[j + 1] + d2d * cells[j] + d3d * cells[j + 2];
+;
+; ...into this:
+;
+;  int j;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = (d1 * cells[j + 1] + d2 * cells[j] + d3 * cells[j + 2]) * delta;
+;
+; We expect the LICM pass to undo this transformation.
+;
+
+
+define void @innermost_loop_3d(i32 %i, i64 %d1, i64 %d2, i64 %d3, i64 %delta, ptr %cells) {
+; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_3d
+; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; REASSOCIATE_ONLY-NEXT:  entry:
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; REASSOCIATE_ONLY:       for.cond:
+; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; REASSOCIATE_ONLY:       for.body:
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_3:%.*]] = mul i64 [[CELL_3]], [[D3]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[FMUL_3]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD1]], [[DELTA]]
+; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J_2]], align 8
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
+; REASSOCIATE_ONLY:       for.end:
+; REASSOCIATE_ONLY-NEXT:    ret void
+;
+; LICM_ONLY-LABEL: define void @innermost_loop_3d
+; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY-NEXT:  entry:
+; LICM_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_ONLY-NEXT:    [[FMUL_D3:%.*]] = mul i64 [[D3]], [[DELTA]]
+; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY:       for.cond:
+; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY:       for.body:
+; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
+; LICM_ONLY-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
+; LICM_ONLY-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; LICM_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_3:%.*]] = mul i64 [[FMUL_D3]], [[CELL_3]]
+; LICM_ONLY-NEXT:    [[FADD_2:%.*]] = add i64 [[FADD_1]], [[FMUL_3]]
+; LICM_ONLY-NEXT:    store i64 [[FADD_2]], ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY:       for.end:
+; LICM_ONLY-NEXT:    ret void
+;
+; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_3d
+; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY_CONSTRAINED-NEXT:  entry:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D3:%.*]] = mul i64 [[D3]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY_CONSTRAINED:       for.cond:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY_CONSTRAINED:       for.body:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_3:%.*]] = mul i64 [[FMUL_D3]], [[CELL_3]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FADD_2:%.*]] = add i64 [[FADD_1]], [[FMUL_3]]
+; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FADD_2]], ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY_CONSTRAINED:       for.end:
+; LICM_ONLY_CONSTRAINED-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_3d
+; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D3]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL2:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL3:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE:       for.cond:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE:       for.body:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL2]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL3]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_3:%.*]] = mul i64 [[CELL_3]], [[FACTOR_OP_MUL]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[FMUL_3]]
+; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_ADD1]], ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE:       for.end:
+; LICM_AFTER_REASSOCIATE-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_3d
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_3:%.*]] = mul i64 [[CELL_3]], [[D3]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[FMUL_3]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD1]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+;
+entry:
+  %fmul.d1 = mul i64 %d1, %delta
+  %fmul.d2 = mul i64 %d2, %delta
+  %fmul.d3 = mul i64 %d3, %delta
+  br label %for.cond
+
+for.cond:
+  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
+  %cmp.not = icmp sgt i32 %j, %i
+  br i1 %cmp.not, label %for.end, label %for.body
+
+for.body:
+  %add.j.1 = add nuw nsw i32 %j, 1
+  %idxprom.j.1 = zext i32 %add.j.1 to i64
+  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
+  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
+  %fmul.1 = mul i64 %fmul.d1, %cell.1
+  %idxprom.j = zext i32 %j to i64
+  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
+  %cell.2 = load i64, ptr %arrayidx.j, align 8
+  %fmul.2 = mul i64 %fmul.d2, %cell.2
+  %fadd.1 = add i64 %fmul.1, %fmul.2
+  %add.j.2 = add nuw nsw i32 %j, 2
+  %idxprom.j.2 = zext i32 %add.j.2 to i64
+  %arrayidx.j.2 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.2
+  %cell.3 = load i64, ptr %arrayidx.j.2, align 8
+  %fmul.3 = mul i64 %fmul.d3, %cell.3
+  %fadd.2 = add i64 %fadd.1, %fmul.3
+  store i64 %fadd.2, ptr %arrayidx.j.2, align 8
+  br label %for.cond
+
+for.end:
+  ret void
+}
+
+;
+; The following loop should be modified by the LICM pass,
+;
+;  int j;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = (d1 * cells[j + 1] + d2 * cells[j]) * delta;
+;
+; ...into this:
+;
+;  int j;
+;  const uint64_t d1d = d1 * delta;
+;  const uint64_t d2d = d2 * delta;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = d1d * cells[j + 1] + d2d * cells[j];
+;
+
+define void @innermost_loop_2d_reassociated(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
+; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_2d_reassociated
+; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; REASSOCIATE_ONLY-NEXT:  entry:
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; REASSOCIATE_ONLY:       for.cond:
+; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; REASSOCIATE_ONLY:       for.body:
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
+; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
+; REASSOCIATE_ONLY:       for.end:
+; REASSOCIATE_ONLY-NEXT:    ret void
+;
+; LICM_ONLY-LABEL: define void @innermost_loop_2d_reassociated
+; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY-NEXT:  entry:
+; LICM_ONLY-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_ONLY-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY:       for.cond:
+; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY:       for.body:
+; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
+; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
+; LICM_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_ONLY-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY:       for.end:
+; LICM_ONLY-NEXT:    ret void
+;
+; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_2d_reassociated
+; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY_CONSTRAINED-NEXT:  entry:
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY_CONSTRAINED:       for.cond:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY_CONSTRAINED:       for.body:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY_CONSTRAINED:       for.end:
+; LICM_ONLY_CONSTRAINED-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_2d_reassociated
+; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE:       for.cond:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE:       for.body:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE:       for.end:
+; LICM_AFTER_REASSOCIATE-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_2d_reassociated
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+;
+entry:
+  br label %for.cond
+
+for.cond:
+  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
+  %cmp.not = icmp sgt i32 %j, %i
+  br i1 %cmp.not, label %for.end, label %for.body
+
+for.body:
+  %add.j.1 = add nuw nsw i32 %j, 1
+  %idxprom.j.1 = zext i32 %add.j.1 to i64
+  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
+  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
+  %fmul.1 = mul i64 %cell.1, %d1
+  %idxprom.j = zext i32 %j to i64
+  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
+  %cell.2 = load i64, ptr %arrayidx.j, align 8
+  %fmul.2 = mul i64 %cell.2, %d2
+  %reass.add = add i64 %fmul.2, %fmul.1
+  %reass.mul = mul i64 %reass.add, %delta
+  store i64 %reass.mul, ptr %arrayidx.j, align 8
+  br label %for.cond
+
+for.end:
+  ret void
+}
+
+;
+; The following loop will not be modified by the LICM pass:
+;
+;  int j;
+;
+;  for (j = 0; j <= i; j++)
+;    cells[j] = (d1 * cells[j + 1] + d2 * cells[j] +
+;                cells[j] * cells[j + 1]) * delta;
+;
+; This case differs as one of the multiplications involves no invariants.
+;
+
+define void @innermost_loop_3d_fast_reassociated_different(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
+; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_3d_fast_reassociated_different
+; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; REASSOCIATE_ONLY-NEXT:  entry:
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; REASSOCIATE_ONLY:       for.cond:
+; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; REASSOCIATE_ONLY:       for.body:
+; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_3:%.*]] = zext i32 [[ADD_J_1]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; REASSOCIATE_ONLY-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
+; REASSOCIATE_ONLY-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[FMUL_1]]
+; REASSOCIATE_ONLY-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[FMUL_2]]
+; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
+; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
+; REASSOCIATE_ONLY:       for.end:
+; REASSOCIATE_ONLY-NEXT:    ret void
+;
+; LICM_ONLY-LABEL: define void @innermost_loop_3d_fast_reassociated_different
+; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY-NEXT:  entry:
+; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY:       for.cond:
+; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY:       for.body:
+; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
+; LICM_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_ONLY-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
+; LICM_ONLY-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[REASS_ADD]]
+; LICM_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
+; LICM_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY:       for.end:
+; LICM_ONLY-NEXT:    ret void
+;
+; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_3d_fast_reassociated_different
+; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_ONLY_CONSTRAINED-NEXT:  entry:
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_ONLY_CONSTRAINED:       for.cond:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_ONLY_CONSTRAINED:       for.body:
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[REASS_ADD]]
+; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
+; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_ONLY_CONSTRAINED:       for.end:
+; LICM_ONLY_CONSTRAINED-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_3d_fast_reassociated_different
+; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE:       for.cond:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE:       for.body:
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[FMUL_2]]
+; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE:       for.end:
+; LICM_AFTER_REASSOCIATE-NEXT:    ret void
+;
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_3d_fast_reassociated_different
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[FMUL_1]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[FMUL_2]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
+; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+;
+entry:
+  br label %for.cond
+
+for.cond:
+  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
+  %cmp.not = icmp sgt i32 %j, %i
+  br i1 %cmp.not, label %for.end, label %for.body
+
+for.body:
+  %add.j.1 = add nuw nsw i32 %j, 1
+  %idxprom.j.1 = zext i32 %add.j.1 to i64
+  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
+  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
+  %add.j.2 = add nuw nsw i32 %j, 2
+  %idxprom.j.2 = zext i32 %add.j.1 to i64
+  %arrayidx.j.2 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.2
+  %cell.2 = load i64, ptr %arrayidx.j.2, align 8
+  %add.j.3 = add nuw nsw i32 %j, 3
+  %idxprom.j.3 = zext i32 %add.j.1 to i64
+  %arrayidx.j.3 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.3
+  %cell.3 = load i64, ptr %arrayidx.j.2, align 8
+  %idxprom.j = zext i32 %j to i64
+  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
+  %cell.4 = load i64, ptr %arrayidx.j, align 8
+  %fmul.1 = mul i64 %cell.1, %d1
+  %fmul.2 = mul i64 %cell.4, %d2
+  %reass.add = add i64 %fmul.2, %fmul.1
+  %extra.mul = mul i64 %cell.3, %cell.2
+  %extra.add = add i64 %extra.mul, %reass.add
+  %reass.mul = mul i64 %extra.add, %delta
+  store i64 %reass.mul, ptr %arrayidx.j, align 8
+  br label %for.cond
+
+for.end:
+  ret void
+}

>From e4fa2d19512d5b7964615120fe67e89a1602b5df Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Fri, 2 Feb 2024 13:39:04 -0800
Subject: [PATCH 2/4] fixup! Simplify test.

---
 .../Transforms/LICM/expr-reassociate-int.ll   | 1086 +++--------------
 1 file changed, 186 insertions(+), 900 deletions(-)

diff --git a/llvm/test/Transforms/LICM/expr-reassociate-int.ll b/llvm/test/Transforms/LICM/expr-reassociate-int.ll
index 556bcfa4dbfd0b..63548974fb3185 100644
--- a/llvm/test/Transforms/LICM/expr-reassociate-int.ll
+++ b/llvm/test/Transforms/LICM/expr-reassociate-int.ll
@@ -1,154 +1,6 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 2
-; RUN: opt -passes='reassociate' -S < %s | FileCheck %s --check-prefix=REASSOCIATE_ONLY
-; RUN: opt -passes='licm' -S < %s | FileCheck %s --check-prefix=LICM_ONLY
-; RUN: opt -passes='licm' -licm-max-num-int-reassociations=1 -S < %s | FileCheck %s --check-prefix=LICM_ONLY_CONSTRAINED
-; RUN: opt -passes='reassociate,loop-mssa(licm)' -S < %s | FileCheck %s --check-prefix=LICM_AFTER_REASSOCIATE
-; RUN: opt -passes='reassociate,loop-mssa(licm)' -licm-max-num-int-reassociations=1 -S < %s | FileCheck %s --check-prefix=LICM_AFTER_REASSOCIATE_CONSTRAINED
-
-;
-; A simple loop, should not get modified:
-;
-;  int j;
-;  const uint64_t d1d = d1 * delta;
-;
-;  for (j = 0; j <= i; j++)
-;    cells[j] = d1d * cells[j + 1];
-;
-
-define void @innermost_loop_1d(i32 %i, i64 %d1, i64 %delta, ptr %cells) {
-; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_1d
-; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; REASSOCIATE_ONLY-NEXT:  entry:
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[DELTA]], [[D1]]
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; REASSOCIATE_ONLY:       for.cond:
-; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; REASSOCIATE_ONLY:       for.body:
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; REASSOCIATE_ONLY-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
-; REASSOCIATE_ONLY:       for.end:
-; REASSOCIATE_ONLY-NEXT:    ret void
-;
-; LICM_ONLY-LABEL: define void @innermost_loop_1d
-; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY-NEXT:  entry:
-; LICM_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY:       for.cond:
-; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY:       for.body:
-; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY:       for.end:
-; LICM_ONLY-NEXT:    ret void
-;
-; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_1d
-; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY_CONSTRAINED-NEXT:  entry:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY_CONSTRAINED:       for.cond:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY_CONSTRAINED:       for.body:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY_CONSTRAINED:       for.end:
-; LICM_ONLY_CONSTRAINED-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_1d
-; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[DELTA]], [[D1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE:       for.cond:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE:       for.body:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE:       for.end:
-; LICM_AFTER_REASSOCIATE-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_1d
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[DELTA]], [[D1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
-;
-entry:
-  %fmul.d1 = mul i64 %d1, %delta
-  br label %for.cond
-
-for.cond:
-  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
-  %cmp.not = icmp sgt i32 %j, %i
-  br i1 %cmp.not, label %for.end, label %for.body
-
-for.body:
-  %add.j.1 = add nuw nsw i32 %j, 1
-  %idxprom.j.1 = zext i32 %add.j.1 to i64
-  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
-  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
-  %fmul.1 = mul i64 %fmul.d1, %cell.1
-  %idxprom.j = zext i32 %j to i64
-  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
-  store i64 %fmul.1, ptr %arrayidx.j, align 8
-  br label %for.cond
-
-for.end:
-  ret void
-}
+; RUN: opt -passes='loop-mssa(licm)' -S < %s | FileCheck %s --check-prefixes=CHECK,NOT_CONSTRAINED
+; RUN: opt -passes='loop-mssa(licm)' -licm-max-num-int-reassociations=1 -S < %s | FileCheck %s --check-prefixes=CHECK,CONSTRAINED
 
 ;
 ; A simple loop:
@@ -168,115 +20,27 @@ for.end:
 ;
 
 define void @innermost_loop_1d_shouldhoist(i32 %i, i64 %d1, i64 %delta, ptr %cells) {
-; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_1d_shouldhoist
-; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; REASSOCIATE_ONLY-NEXT:  entry:
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; REASSOCIATE_ONLY:       for.cond:
-; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; REASSOCIATE_ONLY:       for.body:
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_1]], [[CELL_1]]
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; REASSOCIATE_ONLY-NEXT:    store i64 [[FMUL_2]], ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
-; REASSOCIATE_ONLY:       for.end:
-; REASSOCIATE_ONLY-NEXT:    ret void
-;
-; LICM_ONLY-LABEL: define void @innermost_loop_1d_shouldhoist
-; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY-NEXT:  entry:
-; LICM_ONLY-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY:       for.cond:
-; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY:       for.body:
-; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FACTOR_OP_MUL]], [[CELL_1]]
-; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY:       for.end:
-; LICM_ONLY-NEXT:    ret void
-;
-; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_1d_shouldhoist
-; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY_CONSTRAINED-NEXT:  entry:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY_CONSTRAINED:       for.cond:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY_CONSTRAINED:       for.body:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FACTOR_OP_MUL]], [[CELL_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FMUL_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY_CONSTRAINED:       for.end:
-; LICM_ONLY_CONSTRAINED-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_1d_shouldhoist
-; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE:       for.cond:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE:       for.body:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_1]], [[CELL_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[FMUL_2]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE:       for.end:
-; LICM_AFTER_REASSOCIATE-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_1d_shouldhoist
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_1]], [[CELL_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[FMUL_2]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+; CHECK-LABEL: define void @innermost_loop_1d_shouldhoist
+; CHECK-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[MUL_1:%.*]] = mul i64 [[DELTA]], [[D1]]
+; CHECK-NEXT:    br label [[FOR_COND:%.*]]
+; CHECK:       for.cond:
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; CHECK-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; CHECK:       for.body:
+; CHECK-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; CHECK-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; CHECK-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; CHECK-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; CHECK-NEXT:    [[MUL_2:%.*]] = mul i64 [[MUL_1]], [[CELL_1]]
+; CHECK-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; CHECK-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; CHECK-NEXT:    store i64 [[MUL_2]], ptr [[ARRAYIDX_J]], align 8
+; CHECK-NEXT:    br label [[FOR_COND]]
+; CHECK:       for.end:
+; CHECK-NEXT:    ret void
 ;
 entry:
   br label %for.cond
@@ -291,11 +55,11 @@ for.body:
   %idxprom.j.1 = zext i32 %add.j.1 to i64
   %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
   %cell.1 = load i64, ptr %arrayidx.j.1, align 8
-  %fmul.1 = mul i64 %d1, %cell.1
-  %fmul.2 = mul i64 %fmul.1, %delta
+  %mul.1 = mul i64 %delta, %d1
+  %mul.2 = mul i64 %mul.1, %cell.1
   %idxprom.j = zext i32 %j to i64
   %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
-  store i64 %fmul.2, ptr %arrayidx.j, align 8
+  store i64 %mul.2, ptr %arrayidx.j, align 8
   br label %for.cond
 
 for.end:
@@ -323,137 +87,58 @@ for.end:
 ;
 
 define void @innermost_loop_2d(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
-; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_2d
-; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; REASSOCIATE_ONLY-NEXT:  entry:
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; REASSOCIATE_ONLY:       for.cond:
-; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; REASSOCIATE_ONLY:       for.body:
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
-; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
-; REASSOCIATE_ONLY:       for.end:
-; REASSOCIATE_ONLY-NEXT:    ret void
-;
-; LICM_ONLY-LABEL: define void @innermost_loop_2d
-; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY-NEXT:  entry:
-; LICM_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY:       for.cond:
-; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY:       for.body:
-; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
-; LICM_ONLY-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
-; LICM_ONLY-NEXT:    store i64 [[FADD_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY:       for.end:
-; LICM_ONLY-NEXT:    ret void
-;
-; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_2d
-; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY_CONSTRAINED-NEXT:  entry:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY_CONSTRAINED:       for.cond:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY_CONSTRAINED:       for.body:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FADD_1]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY_CONSTRAINED:       for.end:
-; LICM_ONLY_CONSTRAINED-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_2d
-; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE:       for.cond:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE:       for.body:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE:       for.end:
-; LICM_AFTER_REASSOCIATE-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_2d
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+; NOT_CONSTRAINED-LABEL: define void @innermost_loop_2d
+; NOT_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; NOT_CONSTRAINED-NEXT:  entry:
+; NOT_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
+; NOT_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
+; NOT_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; NOT_CONSTRAINED:       for.cond:
+; NOT_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; NOT_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; NOT_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; NOT_CONSTRAINED:       for.body:
+; NOT_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; NOT_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; NOT_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; NOT_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; NOT_CONSTRAINED-NEXT:    [[MUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
+; NOT_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; NOT_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; NOT_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; NOT_CONSTRAINED-NEXT:    [[MUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
+; NOT_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[MUL_2]], [[MUL_1]]
+; NOT_CONSTRAINED-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
+; NOT_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; NOT_CONSTRAINED:       for.end:
+; NOT_CONSTRAINED-NEXT:    ret void
+;
+; CONSTRAINED-LABEL: define void @innermost_loop_2d
+; CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; CONSTRAINED-NEXT:  entry:
+; CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; CONSTRAINED:       for.cond:
+; CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; CONSTRAINED:       for.body:
+; CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; CONSTRAINED-NEXT:    [[MUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; CONSTRAINED-NEXT:    [[MUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[MUL_2]], [[MUL_1]]
+; CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
+; CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; CONSTRAINED:       for.end:
+; CONSTRAINED-NEXT:    ret void
 ;
 entry:
-  %fmul.d1 = mul i64 %d1, %delta
-  %fmul.d2 = mul i64 %d2, %delta
   br label %for.cond
 
 for.cond:
@@ -466,13 +151,14 @@ for.body:
   %idxprom.j.1 = zext i32 %add.j.1 to i64
   %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
   %cell.1 = load i64, ptr %arrayidx.j.1, align 8
-  %fmul.1 = mul i64 %fmul.d1, %cell.1
+  %mul.1 = mul i64 %cell.1, %d1
   %idxprom.j = zext i32 %j to i64
   %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
   %cell.2 = load i64, ptr %arrayidx.j, align 8
-  %fmul.2 = mul i64 %fmul.d2, %cell.2
-  %fadd.1 = add i64 %fmul.1, %fmul.2
-  store i64 %fadd.1, ptr %arrayidx.j, align 8
+  %mul.2 = mul i64 %cell.2, %d2
+  %reass.add = add i64 %mul.2, %mul.1
+  %reass.mul = mul i64 %reass.add, %delta
+  store i64 %reass.mul, ptr %arrayidx.j, align 8
   br label %for.cond
 
 for.end:
@@ -502,171 +188,71 @@ for.end:
 
 
 define void @innermost_loop_3d(i32 %i, i64 %d1, i64 %d2, i64 %d3, i64 %delta, ptr %cells) {
-; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_3d
-; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; REASSOCIATE_ONLY-NEXT:  entry:
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; REASSOCIATE_ONLY:       for.cond:
-; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; REASSOCIATE_ONLY:       for.body:
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_3:%.*]] = mul i64 [[CELL_3]], [[D3]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[FMUL_3]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD1]], [[DELTA]]
-; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J_2]], align 8
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
-; REASSOCIATE_ONLY:       for.end:
-; REASSOCIATE_ONLY-NEXT:    ret void
-;
-; LICM_ONLY-LABEL: define void @innermost_loop_3d
-; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY-NEXT:  entry:
-; LICM_ONLY-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_ONLY-NEXT:    [[FMUL_D3:%.*]] = mul i64 [[D3]], [[DELTA]]
-; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY:       for.cond:
-; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY:       for.body:
-; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
-; LICM_ONLY-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
-; LICM_ONLY-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
-; LICM_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_3:%.*]] = mul i64 [[FMUL_D3]], [[CELL_3]]
-; LICM_ONLY-NEXT:    [[FADD_2:%.*]] = add i64 [[FADD_1]], [[FMUL_3]]
-; LICM_ONLY-NEXT:    store i64 [[FADD_2]], ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY:       for.end:
-; LICM_ONLY-NEXT:    ret void
-;
-; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_3d
-; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY_CONSTRAINED-NEXT:  entry:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D1:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D2:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_D3:%.*]] = mul i64 [[D3]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY_CONSTRAINED:       for.cond:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY_CONSTRAINED:       for.body:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[FMUL_D1]], [[CELL_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[FMUL_D2]], [[CELL_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FADD_1:%.*]] = add i64 [[FMUL_1]], [[FMUL_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_3:%.*]] = mul i64 [[FMUL_D3]], [[CELL_3]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FADD_2:%.*]] = add i64 [[FADD_1]], [[FMUL_3]]
-; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[FADD_2]], ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY_CONSTRAINED:       for.end:
-; LICM_ONLY_CONSTRAINED-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_3d
-; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D3]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL2:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL3:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE:       for.cond:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE:       for.body:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL2]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL3]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_3:%.*]] = mul i64 [[CELL_3]], [[FACTOR_OP_MUL]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[FMUL_3]]
-; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_ADD1]], ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE:       for.end:
-; LICM_AFTER_REASSOCIATE-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_3d
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_3:%.*]] = mul i64 [[CELL_3]], [[D3]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[FMUL_3]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD1]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+; NOT_CONSTRAINED-LABEL: define void @innermost_loop_3d
+; NOT_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; NOT_CONSTRAINED-NEXT:  entry:
+; NOT_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D3]], [[DELTA]]
+; NOT_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D1]], [[DELTA]]
+; NOT_CONSTRAINED-NEXT:    [[FACTOR_OP_MUL2:%.*]] = mul i64 [[D2]], [[DELTA]]
+; NOT_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; NOT_CONSTRAINED:       for.cond:
+; NOT_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; NOT_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; NOT_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; NOT_CONSTRAINED:       for.body:
+; NOT_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; NOT_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; NOT_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; NOT_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; NOT_CONSTRAINED-NEXT:    [[MUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL1]]
+; NOT_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; NOT_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; NOT_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; NOT_CONSTRAINED-NEXT:    [[MUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL2]]
+; NOT_CONSTRAINED-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; NOT_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; NOT_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; NOT_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; NOT_CONSTRAINED-NEXT:    [[MUL_3:%.*]] = mul i64 [[CELL_3]], [[FACTOR_OP_MUL]]
+; NOT_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[MUL_2]], [[MUL_1]]
+; NOT_CONSTRAINED-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[MUL_3]]
+; NOT_CONSTRAINED-NEXT:    store i64 [[REASS_ADD1]], ptr [[ARRAYIDX_J_2]], align 8
+; NOT_CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; NOT_CONSTRAINED:       for.end:
+; NOT_CONSTRAINED-NEXT:    ret void
+;
+; CONSTRAINED-LABEL: define void @innermost_loop_3d
+; CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[D3:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; CONSTRAINED-NEXT:  entry:
+; CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
+; CONSTRAINED:       for.cond:
+; CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; CONSTRAINED:       for.body:
+; CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; CONSTRAINED-NEXT:    [[MUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; CONSTRAINED-NEXT:    [[MUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
+; CONSTRAINED-NEXT:    [[ADD_J_2:%.*]] = add nuw nsw i32 [[J]], 2
+; CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_2]] to i64
+; CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; CONSTRAINED-NEXT:    [[MUL_3:%.*]] = mul i64 [[CELL_3]], [[D3]]
+; CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[MUL_2]], [[MUL_1]]
+; CONSTRAINED-NEXT:    [[REASS_ADD1:%.*]] = add i64 [[REASS_ADD]], [[MUL_3]]
+; CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD1]], [[DELTA]]
+; CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J_2]], align 8
+; CONSTRAINED-NEXT:    br label [[FOR_COND]]
+; CONSTRAINED:       for.end:
+; CONSTRAINED-NEXT:    ret void
 ;
 entry:
-  %fmul.d1 = mul i64 %d1, %delta
-  %fmul.d2 = mul i64 %d2, %delta
-  %fmul.d3 = mul i64 %d3, %delta
   br label %for.cond
 
 for.cond:
@@ -679,192 +265,20 @@ for.body:
   %idxprom.j.1 = zext i32 %add.j.1 to i64
   %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
   %cell.1 = load i64, ptr %arrayidx.j.1, align 8
-  %fmul.1 = mul i64 %fmul.d1, %cell.1
+  %mul.1 = mul i64 %cell.1, %d1
   %idxprom.j = zext i32 %j to i64
   %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
   %cell.2 = load i64, ptr %arrayidx.j, align 8
-  %fmul.2 = mul i64 %fmul.d2, %cell.2
-  %fadd.1 = add i64 %fmul.1, %fmul.2
+  %mul.2 = mul i64 %cell.2, %d2
   %add.j.2 = add nuw nsw i32 %j, 2
   %idxprom.j.2 = zext i32 %add.j.2 to i64
   %arrayidx.j.2 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.2
   %cell.3 = load i64, ptr %arrayidx.j.2, align 8
-  %fmul.3 = mul i64 %fmul.d3, %cell.3
-  %fadd.2 = add i64 %fadd.1, %fmul.3
-  store i64 %fadd.2, ptr %arrayidx.j.2, align 8
-  br label %for.cond
-
-for.end:
-  ret void
-}
-
-;
-; The following loop should be modified by the LICM pass,
-;
-;  int j;
-;
-;  for (j = 0; j <= i; j++)
-;    cells[j] = (d1 * cells[j + 1] + d2 * cells[j]) * delta;
-;
-; ...into this:
-;
-;  int j;
-;  const uint64_t d1d = d1 * delta;
-;  const uint64_t d2d = d2 * delta;
-;
-;  for (j = 0; j <= i; j++)
-;    cells[j] = d1d * cells[j + 1] + d2d * cells[j];
-;
-
-define void @innermost_loop_2d_reassociated(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
-; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_2d_reassociated
-; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; REASSOCIATE_ONLY-NEXT:  entry:
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; REASSOCIATE_ONLY:       for.cond:
-; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; REASSOCIATE_ONLY:       for.body:
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
-; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
-; REASSOCIATE_ONLY:       for.end:
-; REASSOCIATE_ONLY-NEXT:    ret void
-;
-; LICM_ONLY-LABEL: define void @innermost_loop_2d_reassociated
-; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY-NEXT:  entry:
-; LICM_ONLY-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_ONLY-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY:       for.cond:
-; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY:       for.body:
-; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
-; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
-; LICM_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_ONLY-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY:       for.end:
-; LICM_ONLY-NEXT:    ret void
-;
-; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_2d_reassociated
-; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY_CONSTRAINED-NEXT:  entry:
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY_CONSTRAINED:       for.cond:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY_CONSTRAINED:       for.body:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY_CONSTRAINED:       for.end:
-; LICM_ONLY_CONSTRAINED-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_2d_reassociated
-; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL:%.*]] = mul i64 [[D1]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FACTOR_OP_MUL1:%.*]] = mul i64 [[D2]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE:       for.cond:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE:       for.body:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[FACTOR_OP_MUL]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[FACTOR_OP_MUL1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_ADD]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE:       for.end:
-; LICM_AFTER_REASSOCIATE-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_2d_reassociated
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_2]], [[D2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[REASS_ADD]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
-;
-entry:
-  br label %for.cond
-
-for.cond:
-  %j = phi i32 [ 0, %entry ], [ %add.j.1, %for.body ]
-  %cmp.not = icmp sgt i32 %j, %i
-  br i1 %cmp.not, label %for.end, label %for.body
-
-for.body:
-  %add.j.1 = add nuw nsw i32 %j, 1
-  %idxprom.j.1 = zext i32 %add.j.1 to i64
-  %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
-  %cell.1 = load i64, ptr %arrayidx.j.1, align 8
-  %fmul.1 = mul i64 %cell.1, %d1
-  %idxprom.j = zext i32 %j to i64
-  %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
-  %cell.2 = load i64, ptr %arrayidx.j, align 8
-  %fmul.2 = mul i64 %cell.2, %d2
-  %reass.add = add i64 %fmul.2, %fmul.1
-  %reass.mul = mul i64 %reass.add, %delta
-  store i64 %reass.mul, ptr %arrayidx.j, align 8
+  %mul.3 = mul i64 %cell.3, %d3
+  %reass.add = add i64 %mul.2, %mul.1
+  %reass.add1 = add i64 %reass.add, %mul.3
+  %reass.mul = mul i64 %reass.add1, %delta
+  store i64 %reass.mul, ptr %arrayidx.j.2, align 8
   br label %for.cond
 
 for.end:
@@ -883,162 +297,37 @@ for.end:
 ; This case differs as one of the multiplications involves no invariants.
 ;
 
-define void @innermost_loop_3d_fast_reassociated_different(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
-; REASSOCIATE_ONLY-LABEL: define void @innermost_loop_3d_fast_reassociated_different
-; REASSOCIATE_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; REASSOCIATE_ONLY-NEXT:  entry:
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; REASSOCIATE_ONLY:       for.cond:
-; REASSOCIATE_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; REASSOCIATE_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; REASSOCIATE_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; REASSOCIATE_ONLY:       for.body:
-; REASSOCIATE_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J_3:%.*]] = zext i32 [[ADD_J_1]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; REASSOCIATE_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; REASSOCIATE_ONLY-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; REASSOCIATE_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
-; REASSOCIATE_ONLY-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[FMUL_1]]
-; REASSOCIATE_ONLY-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[FMUL_2]]
-; REASSOCIATE_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
-; REASSOCIATE_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; REASSOCIATE_ONLY-NEXT:    br label [[FOR_COND]]
-; REASSOCIATE_ONLY:       for.end:
-; REASSOCIATE_ONLY-NEXT:    ret void
-;
-; LICM_ONLY-LABEL: define void @innermost_loop_3d_fast_reassociated_different
-; LICM_ONLY-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY-NEXT:  entry:
-; LICM_ONLY-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY:       for.cond:
-; LICM_ONLY-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY:       for.body:
-; LICM_ONLY-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_ONLY-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_ONLY-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
-; LICM_ONLY-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_ONLY-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
-; LICM_ONLY-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[REASS_ADD]]
-; LICM_ONLY-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
-; LICM_ONLY-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY:       for.end:
-; LICM_ONLY-NEXT:    ret void
-;
-; LICM_ONLY_CONSTRAINED-LABEL: define void @innermost_loop_3d_fast_reassociated_different
-; LICM_ONLY_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_ONLY_CONSTRAINED-NEXT:  entry:
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_ONLY_CONSTRAINED:       for.cond:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_ONLY_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_ONLY_CONSTRAINED:       for.body:
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_ONLY_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[FMUL_2]], [[FMUL_1]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[REASS_ADD]]
-; LICM_ONLY_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
-; LICM_ONLY_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_ONLY_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_ONLY_CONSTRAINED:       for.end:
-; LICM_ONLY_CONSTRAINED-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE-LABEL: define void @innermost_loop_3d_fast_reassociated_different
-; LICM_AFTER_REASSOCIATE-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE:       for.cond:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE:       for.body:
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[FMUL_2]]
-; LICM_AFTER_REASSOCIATE-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE:       for.end:
-; LICM_AFTER_REASSOCIATE-NEXT:    ret void
-;
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-LABEL: define void @innermost_loop_3d_fast_reassociated_different
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:  entry:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND:%.*]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.cond:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.body:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[FMUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[FMUL_1]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[FMUL_2]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    br label [[FOR_COND]]
-; LICM_AFTER_REASSOCIATE_CONSTRAINED:       for.end:
-; LICM_AFTER_REASSOCIATE_CONSTRAINED-NEXT:    ret void
+define void @innermost_loop_3d_reassociated_different(i32 %i, i64 %d1, i64 %d2, i64 %delta, ptr %cells) {
+; CHECK-LABEL: define void @innermost_loop_3d_reassociated_different
+; CHECK-SAME: (i32 [[I:%.*]], i64 [[D1:%.*]], i64 [[D2:%.*]], i64 [[DELTA:%.*]], ptr [[CELLS:%.*]]) {
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[FOR_COND:%.*]]
+; CHECK:       for.cond:
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[ADD_J_1:%.*]], [[FOR_BODY:%.*]] ]
+; CHECK-NEXT:    [[CMP_NOT:%.*]] = icmp sgt i32 [[J]], [[I]]
+; CHECK-NEXT:    br i1 [[CMP_NOT]], label [[FOR_END:%.*]], label [[FOR_BODY]]
+; CHECK:       for.body:
+; CHECK-NEXT:    [[ADD_J_1]] = add nuw nsw i32 [[J]], 1
+; CHECK-NEXT:    [[IDXPROM_J_1:%.*]] = zext i32 [[ADD_J_1]] to i64
+; CHECK-NEXT:    [[ARRAYIDX_J_1:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_1]]
+; CHECK-NEXT:    [[CELL_1:%.*]] = load i64, ptr [[ARRAYIDX_J_1]], align 8
+; CHECK-NEXT:    [[IDXPROM_J_2:%.*]] = zext i32 [[ADD_J_1]] to i64
+; CHECK-NEXT:    [[ARRAYIDX_J_2:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J_2]]
+; CHECK-NEXT:    [[CELL_2:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; CHECK-NEXT:    [[CELL_3:%.*]] = load i64, ptr [[ARRAYIDX_J_2]], align 8
+; CHECK-NEXT:    [[IDXPROM_J:%.*]] = zext i32 [[J]] to i64
+; CHECK-NEXT:    [[ARRAYIDX_J:%.*]] = getelementptr inbounds i64, ptr [[CELLS]], i64 [[IDXPROM_J]]
+; CHECK-NEXT:    [[CELL_4:%.*]] = load i64, ptr [[ARRAYIDX_J]], align 8
+; CHECK-NEXT:    [[MUL_1:%.*]] = mul i64 [[CELL_1]], [[D1]]
+; CHECK-NEXT:    [[MUL_2:%.*]] = mul i64 [[CELL_4]], [[D2]]
+; CHECK-NEXT:    [[EXTRA_MUL:%.*]] = mul i64 [[CELL_3]], [[CELL_2]]
+; CHECK-NEXT:    [[REASS_ADD:%.*]] = add i64 [[EXTRA_MUL]], [[MUL_1]]
+; CHECK-NEXT:    [[EXTRA_ADD:%.*]] = add i64 [[REASS_ADD]], [[MUL_2]]
+; CHECK-NEXT:    [[REASS_MUL:%.*]] = mul i64 [[EXTRA_ADD]], [[DELTA]]
+; CHECK-NEXT:    store i64 [[REASS_MUL]], ptr [[ARRAYIDX_J]], align 8
+; CHECK-NEXT:    br label [[FOR_COND]]
+; CHECK:       for.end:
+; CHECK-NEXT:    ret void
 ;
 entry:
   br label %for.cond
@@ -1053,22 +342,19 @@ for.body:
   %idxprom.j.1 = zext i32 %add.j.1 to i64
   %arrayidx.j.1 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.1
   %cell.1 = load i64, ptr %arrayidx.j.1, align 8
-  %add.j.2 = add nuw nsw i32 %j, 2
   %idxprom.j.2 = zext i32 %add.j.1 to i64
   %arrayidx.j.2 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.2
   %cell.2 = load i64, ptr %arrayidx.j.2, align 8
-  %add.j.3 = add nuw nsw i32 %j, 3
   %idxprom.j.3 = zext i32 %add.j.1 to i64
-  %arrayidx.j.3 = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j.3
   %cell.3 = load i64, ptr %arrayidx.j.2, align 8
   %idxprom.j = zext i32 %j to i64
   %arrayidx.j = getelementptr inbounds i64, ptr %cells, i64 %idxprom.j
   %cell.4 = load i64, ptr %arrayidx.j, align 8
-  %fmul.1 = mul i64 %cell.1, %d1
-  %fmul.2 = mul i64 %cell.4, %d2
-  %reass.add = add i64 %fmul.2, %fmul.1
+  %mul.1 = mul i64 %cell.1, %d1
+  %mul.2 = mul i64 %cell.4, %d2
   %extra.mul = mul i64 %cell.3, %cell.2
-  %extra.add = add i64 %extra.mul, %reass.add
+  %reass.add = add i64 %extra.mul, %mul.1
+  %extra.add = add i64 %reass.add, %mul.2
   %reass.mul = mul i64 %extra.add, %delta
   store i64 %reass.mul, ptr %arrayidx.j, align 8
   br label %for.cond

>From 3880028a66736f7ed1a89440055278a6fff6ab99 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Sat, 10 Feb 2024 23:54:07 -0800
Subject: [PATCH 3/4] fixup! Merge int and FP implementations.

---
 llvm/lib/Transforms/Scalar/LICM.cpp | 120 +++++++++-------------------
 1 file changed, 39 insertions(+), 81 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 3e534aa20568e5..63c5c62e9442dd 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2670,21 +2670,31 @@ static bool hoistAddSub(Instruction &I, Loop &L, ICFLoopSafetyInfo &SafetyInfo,
   return false;
 }
 
+static BinaryOperator *isReassociableOp(BinaryOperator *BO, unsigned Opcode1,
+                                        unsigned Opcode2) {
+  if (BO->getOpcode() == Opcode1 || BO->getOpcode() == Opcode2)
+    if (!isa<FPMathOperator>(BO) ||
+        (BO->hasAllowReassoc() && BO->hasNoSignedZeros()))
+      return BO;
+  return nullptr;
+}
+
 /// Try to reassociate expressions like ((A1 * B1) + (A2 * B2) + ...) * C where
 /// A1, A2, ... and C are loop invariants into expressions like
 /// ((A1 * C * B1) + (A2 * C * B2) + ...) and hoist the (A1 * C), (A2 * C), ...
 /// invariant expressions. This functions returns true only if any hoisting has
 /// actually occured.
-static bool hoistFPAssociation(Instruction &I, Loop &L,
-                               ICFLoopSafetyInfo &SafetyInfo,
-                               MemorySSAUpdater &MSSAU, AssumptionCache *AC,
-                               DominatorTree *DT) {
+static bool hoistMulAddAssociation(Instruction &I, Loop &L,
+                                   ICFLoopSafetyInfo &SafetyInfo,
+                                   MemorySSAUpdater &MSSAU, AssumptionCache *AC,
+                                   DominatorTree *DT) {
   using namespace PatternMatch;
-  Value *VariantOp = nullptr, *InvariantOp = nullptr;
 
-  if (!match(&I, m_FMul(m_Value(VariantOp), m_Value(InvariantOp))) ||
-      !I.hasAllowReassoc() || !I.hasNoSignedZeros())
+  if (auto *BO = dyn_cast<BinaryOperator>(&I);
+      !BO || !isReassociableOp(BO, Instruction::Mul, Instruction::FMul))
     return false;
+  Value *VariantOp = I.getOperand(0);
+  Value *InvariantOp = I.getOperand(1);
   if (L.isLoopInvariant(VariantOp))
     std::swap(VariantOp, InvariantOp);
   if (L.isLoopInvariant(VariantOp) || !L.isLoopInvariant(InvariantOp))
@@ -2698,15 +2708,17 @@ static bool hoistFPAssociation(Instruction &I, Loop &L,
     Worklist.push_back(VariantBinOp);
   while (!Worklist.empty()) {
     BinaryOperator *BO = Worklist.pop_back_val();
-    if (!BO->hasOneUse() || !BO->hasAllowReassoc() || !BO->hasNoSignedZeros())
+    if (!BO->hasOneUse())
       return false;
-    BinaryOperator *Op0, *Op1;
-    if (match(BO, m_FAdd(m_BinOp(Op0), m_BinOp(Op1)))) {
-      Worklist.push_back(Op0);
-      Worklist.push_back(Op1);
+    if (isReassociableOp(BO, Instruction::Add, Instruction::FAdd) &&
+        isa<BinaryOperator>(BO->getOperand(0)) &&
+        isa<BinaryOperator>(BO->getOperand(1))) {
+      Worklist.push_back(cast<BinaryOperator>(BO->getOperand(0)));
+      Worklist.push_back(cast<BinaryOperator>(BO->getOperand(1)));
       continue;
     }
-    if (BO->getOpcode() != Instruction::FMul || L.isLoopInvariant(BO))
+    if (!isReassociableOp(BO, Instruction::Mul, Instruction::FMul) ||
+        L.isLoopInvariant(BO))
       return false;
     Use &U0 = BO->getOperandUse(0);
     Use &U1 = BO->getOperandUse(1);
@@ -2716,7 +2728,10 @@ static bool hoistFPAssociation(Instruction &I, Loop &L,
       Changes.push_back(&U1);
     else
       return false;
-    if (Changes.size() > FPAssociationUpperLimit)
+    unsigned Limit = I.getType()->isIntOrIntVectorTy()
+                         ? IntAssociationUpperLimit
+                         : FPAssociationUpperLimit;
+    if (Changes.size() > Limit)
       return false;
   }
   if (Changes.empty())
@@ -2729,66 +2744,12 @@ static bool hoistFPAssociation(Instruction &I, Loop &L,
   for (auto *U : Changes) {
     assert(L.isLoopInvariant(U->get()));
     Instruction *Ins = cast<Instruction>(U->getUser());
-    U->set(Builder.CreateFMulFMF(U->get(), Factor, Ins, "factor.op.fmul"));
-  }
-  I.replaceAllUsesWith(VariantOp);
-  eraseInstruction(I, SafetyInfo, MSSAU);
-  return true;
-}
-
-static bool hoistIntAssociation(Instruction &I, Loop &L,
-                                ICFLoopSafetyInfo &SafetyInfo,
-                                MemorySSAUpdater &MSSAU, AssumptionCache *AC,
-                                DominatorTree *DT) {
-  using namespace PatternMatch;
-  Value *VariantOp = nullptr, *InvariantOp = nullptr;
-
-  if (!match(&I, m_Mul(m_Value(VariantOp), m_Value(InvariantOp))))
-    return false;
-  if (L.isLoopInvariant(VariantOp))
-    std::swap(VariantOp, InvariantOp);
-  if (L.isLoopInvariant(VariantOp) || !L.isLoopInvariant(InvariantOp))
-    return false;
-  Value *Factor = InvariantOp;
-
-  // First, we need to make sure we should do the transformation.
-  SmallVector<Use *> Changes;
-  SmallVector<BinaryOperator *> Worklist;
-  if (BinaryOperator *VariantBinOp = dyn_cast<BinaryOperator>(VariantOp))
-    Worklist.push_back(VariantBinOp);
-  while (!Worklist.empty()) {
-    BinaryOperator *BO = Worklist.pop_back_val();
-    if (!BO->hasOneUse())
-      return false;
-    BinaryOperator *Op0, *Op1;
-    if (match(BO, m_Add(m_BinOp(Op0), m_BinOp(Op1)))) {
-      Worklist.push_back(Op0);
-      Worklist.push_back(Op1);
-      continue;
-    }
-    if (BO->getOpcode() != Instruction::Mul || L.isLoopInvariant(BO))
-      return false;
-    Use &U0 = BO->getOperandUse(0);
-    Use &U1 = BO->getOperandUse(1);
-    if (L.isLoopInvariant(U0))
-      Changes.push_back(&U0);
-    else if (L.isLoopInvariant(U1))
-      Changes.push_back(&U1);
+    Value *Mul;
+    if (I.getType()->isIntOrIntVectorTy())
+      Mul = Builder.CreateMul(U->get(), Factor, "factor.op.mul");
     else
-      return false;
-    if (Changes.size() > IntAssociationUpperLimit)
-      return false;
-  }
-  if (Changes.empty())
-    return false;
-
-  // We know we should do it so let's do the transformation.
-  auto *Preheader = L.getLoopPreheader();
-  assert(Preheader && "Loop is not in simplify form?");
-  IRBuilder<> Builder(Preheader->getTerminator());
-  for (auto *U : Changes) {
-    assert(L.isLoopInvariant(U->get()));
-    U->set(Builder.CreateMul(U->get(), Factor, "factor.op.mul"));
+      Mul = Builder.CreateFMulFMF(U->get(), Factor, Ins, "factor.op.fmul");
+    U->set(Mul);
   }
   I.replaceAllUsesWith(VariantOp);
   eraseInstruction(I, SafetyInfo, MSSAU);
@@ -2822,15 +2783,12 @@ static bool hoistArithmetics(Instruction &I, Loop &L,
     return true;
   }
 
-  if (hoistFPAssociation(I, L, SafetyInfo, MSSAU, AC, DT)) {
-    ++NumHoisted;
-    ++NumFPAssociationsHoisted;
-    return true;
-  }
-
-  if (hoistIntAssociation(I, L, SafetyInfo, MSSAU, AC, DT)) {
+  if (hoistMulAddAssociation(I, L, SafetyInfo, MSSAU, AC, DT)) {
     ++NumHoisted;
-    ++NumIntAssociationsHoisted;
+    if (I.getType()->isIntOrIntVectorTy())
+      ++NumIntAssociationsHoisted;
+    else
+      ++NumFPAssociationsHoisted;
     return true;
   }
 

>From 0ccdfc467f2df0155443c7999a0f47791e5331e9 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 12 Feb 2024 09:40:20 -0800
Subject: [PATCH 4/4] fixup! Make Int and FP opcodes for isReassociableOp
 explicit. Use BinaryOperator::getOperand.

---
 llvm/lib/Transforms/Scalar/LICM.cpp | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/LICM.cpp b/llvm/lib/Transforms/Scalar/LICM.cpp
index 63c5c62e9442dd..8d308a5e70e138 100644
--- a/llvm/lib/Transforms/Scalar/LICM.cpp
+++ b/llvm/lib/Transforms/Scalar/LICM.cpp
@@ -2670,12 +2670,13 @@ static bool hoistAddSub(Instruction &I, Loop &L, ICFLoopSafetyInfo &SafetyInfo,
   return false;
 }
 
-static BinaryOperator *isReassociableOp(BinaryOperator *BO, unsigned Opcode1,
-                                        unsigned Opcode2) {
-  if (BO->getOpcode() == Opcode1 || BO->getOpcode() == Opcode2)
-    if (!isa<FPMathOperator>(BO) ||
-        (BO->hasAllowReassoc() && BO->hasNoSignedZeros()))
-      return BO;
+static BinaryOperator *isReassociableOp(BinaryOperator *BO, unsigned IntOpcode,
+                                        unsigned FPOpcode) {
+  if (BO->getOpcode() == IntOpcode)
+    return BO;
+  if (BO->getOpcode() == FPOpcode && BO->hasAllowReassoc() &&
+      BO->hasNoSignedZeros())
+    return BO;
   return nullptr;
 }
 
@@ -2690,11 +2691,11 @@ static bool hoistMulAddAssociation(Instruction &I, Loop &L,
                                    DominatorTree *DT) {
   using namespace PatternMatch;
 
-  if (auto *BO = dyn_cast<BinaryOperator>(&I);
-      !BO || !isReassociableOp(BO, Instruction::Mul, Instruction::FMul))
+  auto *BO = dyn_cast<BinaryOperator>(&I);
+  if (!BO || !isReassociableOp(BO, Instruction::Mul, Instruction::FMul))
     return false;
-  Value *VariantOp = I.getOperand(0);
-  Value *InvariantOp = I.getOperand(1);
+  Value *VariantOp = BO->getOperand(0);
+  Value *InvariantOp = BO->getOperand(1);
   if (L.isLoopInvariant(VariantOp))
     std::swap(VariantOp, InvariantOp);
   if (L.isLoopInvariant(VariantOp) || !L.isLoopInvariant(InvariantOp))



More information about the llvm-commits mailing list