Index: llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp =================================================================== --- llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp +++ llvm/lib/Transforms/Scalar/LoopInstSimplify.cpp @@ -27,8 +27,10 @@ #include "llvm/IR/Dominators.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/PassManager.h" +#include "llvm/IR/PatternMatch.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" @@ -44,6 +46,95 @@ STATISTIC(NumSimplified, "Number of redundant instructions simplified"); +/// Try to simplify things like (A < INV_1 AND icmp A < INV_2) into (A < +/// min(INV_1, INV_2)), if INV_1 and INV_2 are both loop invariants and their +/// minimun can be computed outside of loop, and X is not a loop-invariant. +static Value *tryToSimplifyAndOr(Instruction &I, Loop &L) { + auto *Preheader = L.getLoopPreheader(); + assert(Preheader && "Loop is not in simplify form?"); + bool Inverse = false; + using namespace PatternMatch; + Value *Cond1, *Cond2; + if (match(&I, m_LogicalOr(m_Value(Cond1), m_Value(Cond2)))) + Inverse = true; + else if (!match(&I, m_LogicalAnd(m_Value(Cond1), m_Value(Cond2)))) + return nullptr; + + auto MatchICmpAgainstInvariant = [&](Value *C, ICmpInst::Predicate &P, + Value *&LHS, Value *&RHS) { + if (!C->hasOneUse()) + return false; + if (!match(C, m_ICmp(P, m_Value(LHS), m_Value(RHS)))) + return false; + if (!ICmpInst::isRelational(P)) + return false; + if (L.isLoopInvariant(LHS) || !L.isLoopInvariant(RHS)) + return false; + if (Inverse) + P = ICmpInst::getInversePredicate(P); + return true; + }; + ICmpInst::Predicate P1, P2; + Value *LHS1, *LHS2, *RHS1, *RHS2; + if (!MatchICmpAgainstInvariant(Cond1, P1, LHS1, RHS1) || + !MatchICmpAgainstInvariant(Cond2, P2, LHS2, RHS2)) + return nullptr; + if (P1 != P2 || LHS1 != LHS2) + return nullptr; + + // Everything is fine, we can do the transform. + bool UseMin = ICmpInst::isLT(P1) || ICmpInst::isLE(P1); + assert( + (UseMin || ICmpInst::isGT(P1) || ICmpInst::isGE(P1)) && + "Relational predicate is either less (or equal) or greater (or equal)!"); + Intrinsic::ID id; + if (ICmpInst::isSigned(P1)) { + if (UseMin) + id = Intrinsic::smin; + else + id = Intrinsic::smax; + } else { + assert(ICmpInst::isUnsigned(P1) && + "All relational predicates are either signed or unsigned!"); + if (UseMin) + id = Intrinsic::umin; + else + id = Intrinsic::umax; + } + IRBuilder<> Builder(Preheader->getTerminator()); + Value *NewRHS = + Builder.CreateBinaryIntrinsic(id, RHS1, RHS2, nullptr, "invariant.rhs"); + Builder.SetInsertPoint(&I); + auto P = P1; + if (Inverse) + P = ICmpInst::getInversePredicate(P); + auto *NewCond = Builder.CreateICmp(P, LHS1, NewRHS, I.getName()); + + return NewCond; +} + +/// Try to make simplification that make sense in presence of loop. For example, +/// they open up opportunities for hoisting invariants. +static Value *tryToSimplifyWithLoop(Instruction &I, Loop &L) { + if (auto *S = tryToSimplifyAndOr(I, L)) + return S; + return nullptr; +} + +/// Try to simplify the instruction with by means possible. +static Value *tryToSimplify(Instruction &I, Loop &L, LoopInfo &LI, + const SimplifyQuery &SQ) { + // Try trivial peephole simplifications. + if (auto *S = simplifyInstruction(&I, SQ.getWithInstruction(&I))) + return S; + if (auto *S = tryToSimplifyWithLoop(I, L)) { + assert(LI.replacementPreservesLCSSAForm(&I, S) && + "Loop simplification are not allowed to break LCSSA!"); + return S; + } + return nullptr; +} + static bool simplifyLoopInst(Loop &L, DominatorTree &DT, LoopInfo &LI, AssumptionCache &AC, const TargetLibraryInfo &TLI, MemorySSAUpdater *MSSAU) { @@ -97,7 +188,7 @@ if (!IsFirstIteration && !ToSimplify->count(&I)) continue; - Value *V = simplifyInstruction(&I, SQ.getWithInstruction(&I)); + Value *V = tryToSimplify(I, L, LI, SQ); if (!V || !LI.replacementPreservesLCSSAForm(&I, V)) continue; Index: llvm/test/Transforms/LoopInstSimplify/loop_invariant.ll =================================================================== --- llvm/test/Transforms/LoopInstSimplify/loop_invariant.ll +++ llvm/test/Transforms/LoopInstSimplify/loop_invariant.ll @@ -2,18 +2,17 @@ ; RUN: opt -S %s -passes=loop-instsimplify | FileCheck %s ; RUN: opt -S %s -passes='loop-mssa(loop-instsimplify)' -verify-memoryssa | FileCheck %s -; TODO: turn to %iv u umax(inv_1, inv_2) and hoist it out of loop. +; turn to %iv >u umax(inv_1, inv_2) and hoist it out of loop. define i32 @test_ugt(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_ugt( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp ugt i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp ugt i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp ugt i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -157,18 +152,17 @@ ret i32 %iv } -; TODO: turn to %iv >=u umax(inv_1, inv_2) and hoist it out of loop. +; turn to %iv >=u umax(inv_1, inv_2) and hoist it out of loop. define i32 @test_uge(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_uge( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp uge i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp uge i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp uge i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -188,18 +182,17 @@ ret i32 %iv } -; TODO: turn to %iv >s smax(inv_1, inv_2) and hoist it out of loop. +; turn to %iv >s smax(inv_1, inv_2) and hoist it out of loop. define i32 @test_sgt(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_sgt( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp sgt i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp sgt i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp sgt i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -219,18 +212,17 @@ ret i32 %iv } -; TODO: turn to %iv >=s smax(inv_1, inv_2) and hoist it out of loop. +; turn to %iv >=s smax(inv_1, inv_2) and hoist it out of loop. define i32 @test_sge(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_sge( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp sge i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = and i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp sge i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -250,18 +242,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_ult_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_ult_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp ult i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp ult i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp ult i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -281,18 +272,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_ule_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_ule_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.umax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp ule i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp ule i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp ule i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -312,18 +302,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_slt_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_slt_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp slt i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp slt i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp slt i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -343,18 +332,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_sle_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_sle_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.smax.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp sle i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp sle i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp sle i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -374,18 +362,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_ugt_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_ugt_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp ugt i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp ugt i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp ugt i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -405,18 +392,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_uge_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_uge_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.umin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp uge i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp uge i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp uge i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -436,18 +422,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_sgt_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_sgt_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp sgt i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp sgt i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp sgt i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]] @@ -467,18 +452,17 @@ ret i32 %iv } -; TODO: Turn OR to AND and handle accordingly. +; Turn OR to AND and handle accordingly. define i32 @test_sge_inv(i32 %start, i32 %inv_1, i32 %inv_2) { ; CHECK-LABEL: @test_sge_inv( ; CHECK-NEXT: entry: +; CHECK-NEXT: [[INVARIANT_RHS:%.*]] = call i32 @llvm.smin.i32(i32 [[INV_1:%.*]], i32 [[INV_2:%.*]]) ; CHECK-NEXT: br label [[LOOP:%.*]] ; CHECK: loop: ; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ] -; CHECK-NEXT: [[CMP_1:%.*]] = icmp sge i32 [[IV]], [[INV_1:%.*]] -; CHECK-NEXT: [[CMP_2:%.*]] = icmp sge i32 [[IV]], [[INV_2:%.*]] -; CHECK-NEXT: [[LOOP_COND:%.*]] = or i1 [[CMP_1]], [[CMP_2]] +; CHECK-NEXT: [[LOOP_COND1:%.*]] = icmp sge i32 [[IV]], [[INVARIANT_RHS]] ; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1 -; CHECK-NEXT: br i1 [[LOOP_COND]], label [[LOOP]], label [[EXIT:%.*]] +; CHECK-NEXT: br i1 [[LOOP_COND1]], label [[LOOP]], label [[EXIT:%.*]] ; CHECK: exit: ; CHECK-NEXT: [[IV_LCSSA:%.*]] = phi i32 [ [[IV]], [[LOOP]] ] ; CHECK-NEXT: ret i32 [[IV_LCSSA]]