forked from OSchip/llvm-project
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
This commit is contained in:
parent
51dbda5438
commit
d9a4a24413
|
@ -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&);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -10,6 +10,7 @@ add_llvm_component_library(LLVMTransformUtils
|
|||
CallPromotionUtils.cpp
|
||||
CallGraphUpdater.cpp
|
||||
CanonicalizeAliases.cpp
|
||||
CanonicalizeFreezeInLoops.cpp
|
||||
CloneFunction.cpp
|
||||
CloneModule.cpp
|
||||
CodeExtractor.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;
|
|
@ -27,6 +27,7 @@ void llvm::initializeTransformUtils(PassRegistry &Registry) {
|
|||
initializeAssumeSimplifyPassLegacyPassPass(Registry);
|
||||
initializeBreakCriticalEdgesPass(Registry);
|
||||
initializeCanonicalizeAliasesLegacyPassPass(Registry);
|
||||
initializeCanonicalizeFreezeInLoopsPass(Registry);
|
||||
initializeInstNamerPass(Registry);
|
||||
initializeLCSSAWrapperPassPass(Registry);
|
||||
initializeLibCallsShrinkWrapLegacyPassPass(Registry);
|
||||
|
|
|
@ -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 }
|
||||
|
||||
@m = global i64 0
|
||||
@h = global %struct.arc* null
|
||||
@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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue