[llvm] d9a4a24 - Add CanonicalizeFreezeInLoops pass

Juneyoung Lee via llvm-commits llvm-commits at lists.llvm.org
Wed May 20 17:29:38 PDT 2020


Author: Juneyoung Lee
Date: 2020-05-21T09:29:29+09:00
New Revision: d9a4a244138c4ec7dcc1b316e2ca57c9c833bf6c

URL: https://github.com/llvm/llvm-project/commit/d9a4a244138c4ec7dcc1b316e2ca57c9c833bf6c
DIFF: https://github.com/llvm/llvm-project/commit/d9a4a244138c4ec7dcc1b316e2ca57c9c833bf6c.diff

LOG: Add CanonicalizeFreezeInLoops pass

Summary:
If an induction variable is frozen and used, SCEV yields imprecise result
because it doesn't say anything about frozen variables.

Due to this reason, performance degradation happened after
https://reviews.llvm.org/D76483 is merged, causing
SCEV yield imprecise result and preventing LSR to optimize a loop.

The suggested solution here is to add a pass which canonicalizes frozen variables
inside a loop. To be specific, it pushes freezes out of the loop by freezing
the initial value and step values instead & dropping nsw/nuw flags from instructions used by freeze.
This solution was also mentioned at https://reviews.llvm.org/D70623 .

Reviewers: spatel, efriedma, lebedev.ri, fhahn, jdoerfert

Reviewed By: fhahn

Subscribers: nikic, mgorny, hiraditya, javed.absar, llvm-commits, sanwou01, nlopes

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D77523

Added: 
    llvm/include/llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h
    llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp
    llvm/test/Transforms/CanonicalizeFreezeInLoops/func_from_mcf_r.ll
    llvm/test/Transforms/CanonicalizeFreezeInLoops/nonsteps-preserve-flags.ll
    llvm/test/Transforms/CanonicalizeFreezeInLoops/onephi.ll
    llvm/test/Transforms/CanonicalizeFreezeInLoops/phis.ll

Modified: 
    llvm/include/llvm/InitializePasses.h
    llvm/include/llvm/Transforms/Utils.h
    llvm/lib/Passes/PassBuilder.cpp
    llvm/lib/Passes/PassRegistry.def
    llvm/lib/Transforms/Utils/CMakeLists.txt
    llvm/lib/Transforms/Utils/Utils.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h
index b1c524fa69bb..13eaad5b3871 100644
--- a/llvm/include/llvm/InitializePasses.h
+++ b/llvm/include/llvm/InitializePasses.h
@@ -91,6 +91,7 @@ void initializeBranchRelaxationPass(PassRegistry&);
 void initializeBreakCriticalEdgesPass(PassRegistry&);
 void initializeBreakFalseDepsPass(PassRegistry&);
 void initializeCanonicalizeAliasesLegacyPassPass(PassRegistry &);
+void initializeCanonicalizeFreezeInLoopsPass(PassRegistry &);
 void initializeCFGOnlyPrinterLegacyPassPass(PassRegistry&);
 void initializeCFGOnlyViewerLegacyPassPass(PassRegistry&);
 void initializeCFGPrinterLegacyPassPass(PassRegistry&);

diff  --git a/llvm/include/llvm/Transforms/Utils.h b/llvm/include/llvm/Transforms/Utils.h
index bdc918b603bc..75edefac1cbd 100644
--- a/llvm/include/llvm/Transforms/Utils.h
+++ b/llvm/include/llvm/Transforms/Utils.h
@@ -154,6 +154,13 @@ FunctionPass *createFixIrreduciblePass();
 // BasicBlock when possible.
 //
 FunctionPass *createAssumeSimplifyPass();
+
+//===----------------------------------------------------------------------===//
+//
+// CanonicalizeFreezeInLoops - Canonicalize freeze instructions in loops so they
+// don't block SCEV.
+//
+Pass *createCanonicalizeFreezeInLoopsPass();
 } // namespace llvm
 
 #endif

diff  --git a/llvm/include/llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h b/llvm/include/llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h
new file mode 100644
index 000000000000..72fdd49a7ff5
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h
@@ -0,0 +1,32 @@
+//==- CanonicalizeFreezeInLoop.h - Canonicalize freezes in a loop-*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file canonicalizes freeze instructions in a loop.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_CANONICALIZE_FREEZES_IN_LOOPS_H
+#define LLVM_TRANSFORMS_UTILS_CANONICALIZE_FREEZES_IN_LOOPS_H
+
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Transforms/Scalar/LoopPassManager.h"
+
+namespace llvm {
+
+/// A pass that canonicalizes freeze instructions in a loop.
+class CanonicalizeFreezeInLoopsPass
+    : public PassInfoMixin<CanonicalizeFreezeInLoopsPass> {
+public:
+  PreservedAnalyses run(Loop &L, LoopAnalysisManager &AM,
+                        LoopStandardAnalysisResults &AR, LPMUpdater &U);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_CANONICALIZE_FREEZES_IN_LOOPS_H

diff  --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index bdde94abaa7a..9b4cc1c7323a 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -179,6 +179,7 @@
 #include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
 #include "llvm/Transforms/Utils/BreakCriticalEdges.h"
 #include "llvm/Transforms/Utils/CanonicalizeAliases.h"
+#include "llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h"
 #include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
 #include "llvm/Transforms/Utils/LCSSA.h"

diff  --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index ec33f8aab796..03c4379f2468 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -316,6 +316,7 @@ LOOP_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
 #ifndef LOOP_PASS
 #define LOOP_PASS(NAME, CREATE_PASS)
 #endif
+LOOP_PASS("canon-freeze", CanonicalizeFreezeInLoopsPass())
 LOOP_PASS("invalidate<all>", InvalidateAllAnalysesPass())
 LOOP_PASS("licm", LICMPass())
 LOOP_PASS("loop-idiom", LoopIdiomRecognizePass())

diff  --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index f327117e32b7..5c26767c1ceb 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -10,6 +10,7 @@ add_llvm_component_library(LLVMTransformUtils
   CallPromotionUtils.cpp
   CallGraphUpdater.cpp
   CanonicalizeAliases.cpp
+  CanonicalizeFreezeInLoops.cpp
   CloneFunction.cpp
   CloneModule.cpp
   CodeExtractor.cpp

diff  --git a/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp b/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp
new file mode 100644
index 000000000000..771c35151ebd
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/CanonicalizeFreezeInLoops.cpp
@@ -0,0 +1,247 @@
+//==- CanonicalizeFreezeInLoops - Canonicalize freezes in a loop-*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This pass canonicalizes freeze instructions in a loop by pushing them out to
+// the preheader.
+//
+//   loop:
+//     i = phi init, i.next
+//     i.next = add nsw i, 1
+//     i.next.fr = freeze i.next // push this out of this loop
+//     use(i.next.fr)
+//     br i1 (i.next <= N), loop, exit
+//   =>
+//     init.fr = freeze init
+//   loop:
+//     i = phi init.fr, i.next
+//     i.next = add i, 1         // nsw is dropped here
+//     use(i.next)
+//     br i1 (i.next <= N), loop, exit
+//
+// Removing freezes from these chains help scalar evolution successfully analyze
+// expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/CanonicalizeFreezeInLoops.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Analysis/IVUsers.h"
+#include "llvm/Analysis/LoopAnalysisManager.h"
+#include "llvm/Analysis/LoopInfo.h"
+#include "llvm/Analysis/LoopPass.h"
+#include "llvm/Analysis/ValueTracking.h"
+#include "llvm/InitializePasses.h"
+#include "llvm/Pass.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/Utils.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "canon-freeze"
+
+namespace {
+
+class CanonicalizeFreezeInLoops : public LoopPass {
+public:
+  static char ID;
+
+  CanonicalizeFreezeInLoops();
+
+private:
+  bool runOnLoop(Loop *L, LPPassManager &LPM) override;
+  void getAnalysisUsage(AnalysisUsage &AU) const override;
+};
+
+class CanonicalizeFreezeInLoopsImpl {
+  Loop *L;
+  ScalarEvolution &SE;
+  DominatorTree &DT;
+
+  struct FrozenIndPHIInfo {
+    // A freeze instruction that uses an induction phi
+    FreezeInst *FI = nullptr;
+    // The induction phi, step instruction, the operand idx of StepInst which is
+    // a step value
+    PHINode *PHI;
+    BinaryOperator *StepInst;
+    unsigned StepValIdx = 0;
+
+    FrozenIndPHIInfo(PHINode *PHI, BinaryOperator *StepInst)
+        : PHI(PHI), StepInst(StepInst) {}
+  };
+
+  // Can freeze instruction be pushed into operands of I?
+  // In order to do this, I should not create a poison after I's flags are
+  // stripped.
+  bool canHandleInst(const Instruction *I) {
+    auto Opc = I->getOpcode();
+    // If add/sub/mul, drop nsw/nuw flags.
+    return Opc == Instruction::Add || Opc == Instruction::Sub ||
+           Opc == Instruction::Mul;
+  }
+
+  void InsertFreezeAndForgetFromSCEV(Use &U);
+
+public:
+  CanonicalizeFreezeInLoopsImpl(Loop *L, ScalarEvolution &SE, DominatorTree &DT)
+      : L(L), SE(SE), DT(DT) {}
+  bool run();
+};
+
+} // anonymous namespace
+
+// Given U = (value, user), replace value with freeze(value), and let
+// SCEV forget user. The inserted freeze is placed in the preheader.
+void CanonicalizeFreezeInLoopsImpl::InsertFreezeAndForgetFromSCEV(Use &U) {
+  auto *PH = L->getLoopPreheader();
+
+  auto *UserI = cast<Instruction>(U.getUser());
+  auto *ValueToFr = U.get();
+  assert(L->contains(UserI->getParent()) &&
+         "Should not process an instruction that isn't inside the loop");
+  if (isGuaranteedNotToBeUndefOrPoison(ValueToFr, UserI, &DT))
+    return;
+
+  LLVM_DEBUG(dbgs() << "canonfr: inserting freeze:\n");
+  LLVM_DEBUG(dbgs() << "\tUser: " << *U.getUser() << "\n");
+  LLVM_DEBUG(dbgs() << "\tOperand: " << *U.get() << "\n");
+
+  U.set(new FreezeInst(ValueToFr, ValueToFr->getName() + ".frozen",
+                       PH->getTerminator()));
+
+  SE.forgetValue(UserI);
+}
+
+bool CanonicalizeFreezeInLoopsImpl::run() {
+  // The loop should be in LoopSimplify form.
+  if (!L->isLoopSimplifyForm())
+    return false;
+
+  SmallVector<FrozenIndPHIInfo, 4> Candidates;
+
+  for (auto &PHI : L->getHeader()->phis()) {
+    InductionDescriptor ID;
+    if (!InductionDescriptor::isInductionPHI(&PHI, L, &SE, ID))
+      continue;
+
+    LLVM_DEBUG(dbgs() << "canonfr: PHI: " << PHI << "\n");
+    FrozenIndPHIInfo Info(&PHI, ID.getInductionBinOp());
+    if (!Info.StepInst || !canHandleInst(Info.StepInst)) {
+      // The stepping instruction has unknown form.
+      // Ignore this PHI.
+      continue;
+    }
+
+    Info.StepValIdx = Info.StepInst->getOperand(0) == &PHI;
+    Value *StepV = Info.StepInst->getOperand(Info.StepValIdx);
+    if (auto *StepI = dyn_cast<Instruction>(StepV)) {
+      if (L->contains(StepI->getParent())) {
+        // The step value is inside the loop. Freezing step value will introduce
+        // another freeze into the loop, so skip this PHI.
+        continue;
+      }
+    }
+
+    auto Visit = [&](User *U) {
+      if (auto *FI = dyn_cast<FreezeInst>(U)) {
+        LLVM_DEBUG(dbgs() << "canonfr: found: " << *FI << "\n");
+        Info.FI = FI;
+        Candidates.push_back(Info);
+      }
+    };
+    for_each(PHI.users(), Visit);
+    for_each(Info.StepInst->users(), Visit);
+  }
+
+  if (Candidates.empty())
+    return false;
+
+  SmallSet<PHINode *, 8> ProcessedPHIs;
+  for (const auto &Info : Candidates) {
+    PHINode *PHI = Info.PHI;
+    if (!ProcessedPHIs.insert(Info.PHI).second)
+      continue;
+
+    BinaryOperator *StepI = Info.StepInst;
+    assert(StepI && "Step instruction should have been found");
+
+    // Drop flags from the step instruction.
+    if (!isGuaranteedNotToBeUndefOrPoison(StepI, StepI, &DT)) {
+      LLVM_DEBUG(dbgs() << "canonfr: drop flags: " << *StepI << "\n");
+      StepI->dropPoisonGeneratingFlags();
+      SE.forgetValue(StepI);
+    }
+
+    InsertFreezeAndForgetFromSCEV(StepI->getOperandUse(Info.StepValIdx));
+
+    unsigned OperandIdx =
+        PHI->getOperandNumForIncomingValue(PHI->getIncomingValue(0) == StepI);
+    InsertFreezeAndForgetFromSCEV(PHI->getOperandUse(OperandIdx));
+  }
+
+  // Finally, remove the old freeze instructions.
+  for (const auto &Item : Candidates) {
+    auto *FI = Item.FI;
+    LLVM_DEBUG(dbgs() << "canonfr: removing " << *FI << "\n");
+    SE.forgetValue(FI);
+    FI->replaceAllUsesWith(FI->getOperand(0));
+    FI->eraseFromParent();
+  }
+
+  return true;
+}
+
+CanonicalizeFreezeInLoops::CanonicalizeFreezeInLoops() : LoopPass(ID) {
+  initializeCanonicalizeFreezeInLoopsPass(*PassRegistry::getPassRegistry());
+}
+
+void CanonicalizeFreezeInLoops::getAnalysisUsage(AnalysisUsage &AU) const {
+  AU.addPreservedID(LoopSimplifyID);
+  AU.addRequired<LoopInfoWrapperPass>();
+  AU.addPreserved<LoopInfoWrapperPass>();
+  AU.addRequiredID(LoopSimplifyID);
+  AU.addRequired<ScalarEvolutionWrapperPass>();
+  AU.addPreserved<ScalarEvolutionWrapperPass>();
+  AU.addRequired<DominatorTreeWrapperPass>();
+  AU.addPreserved<DominatorTreeWrapperPass>();
+}
+
+bool CanonicalizeFreezeInLoops::runOnLoop(Loop *L, LPPassManager &) {
+  if (skipLoop(L))
+    return false;
+
+  auto &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
+  auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
+  return CanonicalizeFreezeInLoopsImpl(L, SE, DT).run();
+}
+
+PreservedAnalyses
+CanonicalizeFreezeInLoopsPass::run(Loop &L, LoopAnalysisManager &AM,
+                                   LoopStandardAnalysisResults &AR,
+                                   LPMUpdater &U) {
+  if (!CanonicalizeFreezeInLoopsImpl(&L, AR.SE, AR.DT).run())
+    return PreservedAnalyses::all();
+
+  return getLoopPassPreservedAnalyses();
+}
+
+INITIALIZE_PASS_BEGIN(CanonicalizeFreezeInLoops, "canon-freeze",
+                      "Canonicalize Freeze Instructions in Loops", false, false)
+INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(ScalarEvolutionWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(LoopSimplify)
+INITIALIZE_PASS_END(CanonicalizeFreezeInLoops, "canon-freeze",
+                    "Canonicalize Freeze Instructions in Loops", false, false)
+
+Pass *llvm::createCanonicalizeFreezeInLoopsPass() {
+  return new CanonicalizeFreezeInLoops();
+}
+
+char CanonicalizeFreezeInLoops::ID = 0;

diff  --git a/llvm/lib/Transforms/Utils/Utils.cpp b/llvm/lib/Transforms/Utils/Utils.cpp
index 6fa26369d9c1..fc1e824aadea 100644
--- a/llvm/lib/Transforms/Utils/Utils.cpp
+++ b/llvm/lib/Transforms/Utils/Utils.cpp
@@ -27,6 +27,7 @@ void llvm::initializeTransformUtils(PassRegistry &Registry) {
   initializeAssumeSimplifyPassLegacyPassPass(Registry);
   initializeBreakCriticalEdgesPass(Registry);
   initializeCanonicalizeAliasesLegacyPassPass(Registry);
+  initializeCanonicalizeFreezeInLoopsPass(Registry);
   initializeInstNamerPass(Registry);
   initializeLCSSAWrapperPassPass(Registry);
   initializeLibCallsShrinkWrapLegacyPassPass(Registry);

diff  --git a/llvm/test/Transforms/CanonicalizeFreezeInLoops/func_from_mcf_r.ll b/llvm/test/Transforms/CanonicalizeFreezeInLoops/func_from_mcf_r.ll
new file mode 100644
index 000000000000..44c931bb578e
--- /dev/null
+++ b/llvm/test/Transforms/CanonicalizeFreezeInLoops/func_from_mcf_r.ll
@@ -0,0 +1,71 @@
+; RUN: opt < %s -canon-freeze -S | FileCheck %s
+; REQUIRES: aarch64-registered-target
+target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
+target triple = "aarch64-unknown-linux-gnu"
+
+%struct.arc = type { i32 }
+%struct.g = type { i64, %struct.arc, i64, i64, i64 }
+
+ at m = global i64 0
+ at h = global %struct.arc* null
+ at j = global %struct.g zeroinitializer
+
+define dso_local i32 @main() {
+bb:
+  %tmp = load i64, i64* getelementptr inbounds (%struct.g, %struct.g* @j, i32 0, i32 0), align 8
+  %tmp1 = icmp sgt i64 %tmp, 0
+  br i1 %tmp1, label %bb2, label %bb35
+
+bb2:                                              ; preds = %bb
+  %tmp3 = load i64, i64* @m, align 8
+  %tmp4 = load %struct.arc*, %struct.arc** @h, align 8
+; CHECK: %tmp3.frozen = freeze i64 %tmp3
+  br label %bb5
+
+bb5:                                              ; preds = %bb28, %bb2
+  %tmp6 = phi %struct.arc* [ %tmp4, %bb2 ], [ %tmp31, %bb28 ]
+  %tmp7 = phi i64 [ %tmp3, %bb2 ], [ %tmp12, %bb28 ]
+; CHECK: %tmp7 = phi i64 [ %tmp3.frozen, %bb2 ], [ %tmp12, %bb28 ]
+  %tmp8 = phi i64 [ 0, %bb2 ], [ %tmp11, %bb28 ]
+  %tmp9 = trunc i64 %tmp7 to i32
+  %tmp10 = getelementptr inbounds %struct.arc, %struct.arc* %tmp6, i64 0, i32 0
+  store i32 %tmp9, i32* %tmp10, align 4
+  %tmp11 = add nuw nsw i64 %tmp8, 1
+  %tmp12 = add nsw i64 %tmp7, 1
+; CHECK: %tmp12 = add i64 %tmp7, 1
+  store i64 %tmp12, i64* @m, align 8
+  %tmp13 = load i64, i64* inttoptr (i64 16 to i64*), align 16
+  %tmp14 = freeze i64 %tmp12
+; CHECK-NOT: %tmp14 = freeze i64 %tmp12
+  %tmp15 = freeze i64 %tmp13
+  %tmp16 = sdiv i64 %tmp14, %tmp15
+  %tmp17 = mul i64 %tmp16, %tmp15
+  %tmp18 = sub i64 %tmp14, %tmp17
+  %tmp19 = load i64, i64* inttoptr (i64 24 to i64*), align 8
+  %tmp20 = icmp sgt i64 %tmp18, %tmp19
+  %tmp21 = load i64, i64* inttoptr (i64 32 to i64*), align 32
+  br i1 %tmp20, label %bb22, label %bb28
+
+bb22:                                             ; preds = %bb5
+  %tmp23 = mul nsw i64 %tmp21, %tmp19
+  %tmp24 = sub nsw i64 %tmp18, %tmp19
+  %tmp25 = add nsw i64 %tmp21, -1
+  %tmp26 = mul nsw i64 %tmp25, %tmp24
+  %tmp27 = add nsw i64 %tmp26, %tmp23
+  br label %bb28
+
+bb28:                                             ; preds = %bb22, %bb5
+  %tmp29 = phi i64 [ %tmp27, %bb22 ], [ %tmp21, %bb5 ]
+  %tmp30 = add nsw i64 %tmp29, %tmp16
+  %tmp31 = getelementptr inbounds %struct.arc, %struct.arc* getelementptr inbounds (%struct.g, %struct.g* @j, i32 0, i32 1), i64 %tmp30
+  store %struct.arc* %tmp31, %struct.arc** @h, align 8
+  %tmp32 = load i64, i64* getelementptr inbounds (%struct.g, %struct.g* @j, i32 0, i32 0), align 8
+  %tmp33 = icmp slt i64 %tmp11, %tmp32
+  br i1 %tmp33, label %bb5, label %bb34
+
+bb34:                                             ; preds = %bb28
+  br label %bb35
+
+bb35:                                             ; preds = %bb34, %bb
+  ret i32 0
+}

diff  --git a/llvm/test/Transforms/CanonicalizeFreezeInLoops/nonsteps-preserve-flags.ll b/llvm/test/Transforms/CanonicalizeFreezeInLoops/nonsteps-preserve-flags.ll
new file mode 100644
index 000000000000..44998ef880b8
--- /dev/null
+++ b/llvm/test/Transforms/CanonicalizeFreezeInLoops/nonsteps-preserve-flags.ll
@@ -0,0 +1,34 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -canon-freeze -S | FileCheck %s
+declare void @call(i32)
+
+define void @add(i32 %init, i32 %n) {
+; CHECK-LABEL: @add(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[NONSTEP:%.*]] = mul nsw i32 [[I]], 2
+; CHECK-NEXT:    call void @call(i32 [[NONSTEP]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry], [%i.next, %loop ]
+  %i.next = add nsw i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  %nonstep = mul nsw i32 %i, 2
+  call void @call(i32 %nonstep)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}

diff  --git a/llvm/test/Transforms/CanonicalizeFreezeInLoops/onephi.ll b/llvm/test/Transforms/CanonicalizeFreezeInLoops/onephi.ll
new file mode 100644
index 000000000000..ca7ed4d71133
--- /dev/null
+++ b/llvm/test/Transforms/CanonicalizeFreezeInLoops/onephi.ll
@@ -0,0 +1,547 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -canon-freeze -S | FileCheck %s
+; A set of tests that have one phi node
+declare void @call(i32)
+declare i32 @get_step()
+
+define void @add(i32 %init, i32 %n) {
+; CHECK-LABEL: @add(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry], [%i.next, %loop ]
+  %i.next = add i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @add_comm(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_comm(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 1, [[I]]
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add i32 1, %i
+  %i.next.fr = freeze i32 %i.next
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @add_multiuses(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_multiuses(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @add_multiuses2(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_multiuses2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %i.next.fr2 = freeze i32 %i.next
+  call void @call(i32 %i.next.fr2)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @add_flags(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_flags(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @add_ind(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_ind(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[I_FR_NEXT:%.*]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_FR_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_FR_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %i.fr = freeze i32 %i
+  %i.fr.next = add nuw nsw i32 %i.fr, 1
+  call void @call(i32 %i.fr.next)
+  %cond = icmp eq i32 %i.fr.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+define void @add_ind_frozen(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_ind_frozen(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT_FR:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_FR:%.*]] = freeze i32 [[I]]
+; CHECK-NEXT:    [[I_NEXT_FR]] = add nuw nsw i32 [[I_FR]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT_FR]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT_FR]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [%init, %entry], [%i.next.fr, %loop]
+  %i.fr = freeze i32 %i
+  %i.next.fr = add nuw nsw i32 %i.fr, 1
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @add_flags_not_compared(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_flags_not_compared(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+define void @add_flags_not_compared_stepinst(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_flags_not_compared_stepinst(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT_FR:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT:%.*]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[I_NEXT_FR]] = freeze i32 [[I_NEXT]]
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT_FR]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next.fr, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+; If pushing freeze through icmp is needed, this should be enabled.
+; There is no correctness issue in pushing freeze into icmp here, just it's
+; being conservative right now.
+define void @add_flags_stepinst_frozen(i32 %init, i32 %n) {
+; CHECK-LABEL: @add_flags_stepinst_frozen(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    [[COND_FR:%.*]] = freeze i1 [[COND]]
+; CHECK-NEXT:    br i1 [[COND_FR]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  call void @call(i32 %i.next)
+  %cond = icmp eq i32 %i.next, %n
+  %cond.fr = freeze i1 %cond
+  br i1 %cond.fr, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @sub(i32 %init, i32 %n) {
+; CHECK-LABEL: @sub(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = sub i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [%init, %entry], [%i.next, %loop]
+  %i.next = sub nuw nsw i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @init_const(i32 %n) {
+; CHECK-LABEL: @init_const(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ 0, %entry ], [ %i.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @step_init_arg(i32 %init, i32 %n, i32 %step) {
+; CHECK-LABEL: @step_init_arg(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[STEP_FROZEN:%.*]] = freeze i32 [[STEP:%.*]]
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], [[STEP_FROZEN]]
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [%init, %entry], [%i.next, %loop]
+  %i.next = add nuw nsw i32 %i, %step
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @step_init_arg_multiuses(i32 %init, i32 %n, i32 %step) {
+; CHECK-LABEL: @step_init_arg_multiuses(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[STEP_FROZEN:%.*]] = freeze i32 [[STEP:%.*]]
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], [[STEP_FROZEN]]
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.next = add nsw nuw i32 %i, %step
+  %i.next.fr1 = freeze i32 %i.next
+  call void @call(i32 %i.next.fr1)
+  %i.next.fr2 = freeze i32 %i.next
+  call void @call(i32 %i.next.fr2)
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @step_init_arg_multiuses2(i32 %init, i32 %n, i32 %step) {
+; CHECK-LABEL: @step_init_arg_multiuses2(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[STEP_FROZEN:%.*]] = freeze i32 [[STEP:%.*]]
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    call void @call(i32 [[I]])
+; CHECK-NEXT:    call void @call(i32 [[I]])
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], [[STEP_FROZEN]]
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %init, %entry ], [ %i.next, %loop ]
+  %i.fr1 = freeze i32 %i
+  call void @call(i32 %i.fr1)
+  %i.fr2 = freeze i32 %i
+  call void @call(i32 %i.fr2)
+  %i.next = add nsw nuw i32 %i, %step
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @step_init_inst(i32 %n) {
+; CHECK-LABEL: @step_init_inst(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[STEP:%.*]] = call i32 @get_step()
+; CHECK-NEXT:    [[INIT:%.*]] = call i32 @get_step()
+; CHECK-NEXT:    [[STEP_FROZEN:%.*]] = freeze i32 [[STEP]]
+; CHECK-NEXT:    [[INIT_FROZEN:%.*]] = freeze i32 [[INIT]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], [[STEP_FROZEN]]
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %step = call i32 @get_step()
+  %init = call i32 @get_step()
+  br label %loop
+
+loop:
+  %i = phi i32 [%init, %entry], [%i.next, %loop]
+  %i.next = add nuw nsw i32 %i, %step
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+define void @step_inst(i32 %init, i32 %n) {
+; CHECK-LABEL: @step_inst(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], [[I]]
+; CHECK-NEXT:    [[I_NEXT_FR:%.*]] = freeze i32 [[I_NEXT]]
+; CHECK-NEXT:    call void @call(i32 [[I_NEXT_FR]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT_FR]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [%init, %entry], [%i.next, %loop]
+  %i.next = add nuw nsw i32 %i, %i
+  %i.next.fr = freeze i32 %i.next
+  call void @call(i32 %i.next.fr)
+  %cond = icmp eq i32 %i.next.fr, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+define void @gep(i8* %init, i8* %end) {
+; CHECK-LABEL: @gep(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i8* [ [[INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = getelementptr inbounds i8, i8* [[I]], i64 1
+; CHECK-NEXT:    [[I_NEXT_FR:%.*]] = freeze i8* [[I_NEXT]]
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i8* [[I_NEXT_FR]], [[END:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i8* [ %init, %entry], [%i.next, %loop ]
+  %i.next = getelementptr inbounds i8, i8* %i, i64 1
+  %i.next.fr = freeze i8* %i.next
+  %cond = icmp eq i8* %i.next.fr, %end
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}

diff  --git a/llvm/test/Transforms/CanonicalizeFreezeInLoops/phis.ll b/llvm/test/Transforms/CanonicalizeFreezeInLoops/phis.ll
new file mode 100644
index 000000000000..333f754dc5bc
--- /dev/null
+++ b/llvm/test/Transforms/CanonicalizeFreezeInLoops/phis.ll
@@ -0,0 +1,114 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -canon-freeze -S | FileCheck %s
+; A set of tests that have several phi nodes
+declare void @call(i32)
+declare i32 @call2()
+
+define void @onephi_used(i32 %n, i32 %i.init, i32 %j.init) {
+; CHECK-LABEL: @onephi_used(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    [[I_INIT_FROZEN:%.*]] = freeze i32 [[I_INIT:%.*]]
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_INIT_FROZEN]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ [[J_INIT:%.*]], [[ENTRY]] ], [ [[J_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add i32 [[I]], 1
+; CHECK-NEXT:    [[J_NEXT]] = add nuw nsw i32 [[J]], -2
+; CHECK-NEXT:    call void @call(i32 [[I]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+loop:
+  %i = phi i32 [ %i.init, %entry ], [ %i.next, %loop ]
+  %j = phi i32 [ %j.init, %entry ], [ %j.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %j.next = add nuw nsw i32 %j, -2
+  %i.fr = freeze i32 %i
+  call void @call(i32 %i.fr)
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+define void @twophis_used(i32 %n, i32 %i.init, i32 %j.init) {
+; CHECK-LABEL: @twophis_used(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ [[J_INIT:%.*]], [[ENTRY]] ], [ [[J_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[J_NEXT]] = add nuw nsw i32 [[J]], -2
+; CHECK-NEXT:    [[IJ:%.*]] = add i32 [[I]], [[J]]
+; CHECK-NEXT:    [[IJ_FR:%.*]] = freeze i32 [[IJ]]
+; CHECK-NEXT:    call void @call(i32 [[IJ_FR]])
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+loop:
+  %i = phi i32 [ %i.init, %entry ], [ %i.next, %loop ]
+  %j = phi i32 [ %j.init, %entry ], [ %j.next, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %j.next = add nuw nsw i32 %j, -2
+  %ij = add i32 %i, %j
+  %ij.fr = freeze i32 %ij
+  call void @call(i32 %ij.fr)
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+; Negative test
+define void @nonindphi_used(i32 %n, i32 %i.init, i32 %j.init, i32 %k.init) {
+; CHECK-LABEL: @nonindphi_used(
+; CHECK-NEXT:  entry:
+; CHECK-NEXT:    br label [[LOOP:%.*]]
+; CHECK:       loop:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ [[I_INIT:%.*]], [[ENTRY:%.*]] ], [ [[I_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ [[J_INIT:%.*]], [[ENTRY]] ], [ [[J_NEXT:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[K:%.*]] = phi i32 [ [[K_INIT:%.*]], [[ENTRY]] ], [ [[ANY:%.*]], [[LOOP]] ]
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[J_NEXT]] = add nuw nsw i32 [[J]], -2
+; CHECK-NEXT:    [[IJ:%.*]] = add i32 [[I]], [[J]]
+; CHECK-NEXT:    [[IJK:%.*]] = add i32 [[IJ]], [[K]]
+; CHECK-NEXT:    [[IJK_FR:%.*]] = freeze i32 [[IJK]]
+; CHECK-NEXT:    call void @call(i32 [[IJK_FR]])
+; CHECK-NEXT:    [[ANY]] = call i32 @call2()
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[I_NEXT]], [[N:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT:%.*]]
+; CHECK:       exit:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop
+
+loop:
+  %i = phi i32 [ %i.init, %entry ], [ %i.next, %loop ]
+  %j = phi i32 [ %j.init, %entry ], [ %j.next, %loop ]
+  %k = phi i32 [ %k.init, %entry ], [ %any, %loop ]
+  %i.next = add nuw nsw i32 %i, 1
+  %j.next = add nuw nsw i32 %j, -2
+  %ij = add i32 %i, %j
+  %ijk = add i32 %ij, %k
+  %ijk.fr = freeze i32 %ijk
+  call void @call(i32 %ijk.fr)
+  %any = call i32 @call2()
+  %cond = icmp eq i32 %i.next, %n
+  br i1 %cond, label %loop, label %exit
+
+exit:
+  ret void
+}


        


More information about the llvm-commits mailing list