diff --git a/llvm/include/llvm/Analysis/ConstraintSystem.h b/llvm/include/llvm/Analysis/ConstraintSystem.h --- a/llvm/include/llvm/Analysis/ConstraintSystem.h +++ b/llvm/include/llvm/Analysis/ConstraintSystem.h @@ -44,6 +44,21 @@ NumVariables = (uint32_t)std::max((size_t)NumVariables, R.size()); } + void addVariableRowFill(const SmallVector &R) { + for (auto &CR : Constraints) { + while (CR.size() != R.size()) + CR.push_back(0); + } + assert(Constraints.empty() || R.size() == Constraints.back().size()); + for (auto &C : R) { + auto A = std::abs(C); + GCD = APIntOps::GreatestCommonDivisor({32, (uint32_t)A}, {32, GCD}) + .getZExtValue(); + } + Constraints.push_back(R); + NumVariables = (uint32_t)std::max((size_t)NumVariables, R.size()); + } + /// Retuns true if there may be a solution for the constraints in the system. bool mayHaveSolution(); @@ -57,6 +72,8 @@ } bool isConditionImplied(SmallVector R); + + void popLastConstraint() { Constraints.pop_back(); } }; } // namespace llvm diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -114,6 +114,7 @@ void initializeConstantHoistingLegacyPassPass(PassRegistry&); void initializeConstantMergeLegacyPassPass(PassRegistry&); void initializeConstantPropagationPass(PassRegistry&); +void initializeConstraintEliminationPass(PassRegistry &); void initializeControlHeightReductionLegacyPassPass(PassRegistry&); void initializeCorrelatedValuePropagationPass(PassRegistry&); void initializeCostModelAnalysisPass(PassRegistry&); diff --git a/llvm/include/llvm/Transforms/Scalar.h b/llvm/include/llvm/Transforms/Scalar.h --- a/llvm/include/llvm/Transforms/Scalar.h +++ b/llvm/include/llvm/Transforms/Scalar.h @@ -344,6 +344,11 @@ // FunctionPass *createConstantHoistingPass(); +//===----------------------------------------------------------------------===// +// +// +FunctionPass *createConstraintEliminationPass(); + //===----------------------------------------------------------------------===// // // Sink - Code Sinking diff --git a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp --- a/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ b/llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -381,6 +381,8 @@ } } + MPM.add(createConstraintEliminationPass()); + if (OptLevel > 1) { // Speculative execution if the target has divergent branches; otherwise nop. MPM.add(createSpeculativeExecutionIfHasBranchDivergencePass()); diff --git a/llvm/lib/Transforms/Scalar/CMakeLists.txt b/llvm/lib/Transforms/Scalar/CMakeLists.txt --- a/llvm/lib/Transforms/Scalar/CMakeLists.txt +++ b/llvm/lib/Transforms/Scalar/CMakeLists.txt @@ -5,6 +5,7 @@ CallSiteSplitting.cpp ConstantHoisting.cpp ConstantProp.cpp + ConstraintElimination.cpp CorrelatedValuePropagation.cpp DCE.cpp DeadStoreElimination.cpp diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -0,0 +1,407 @@ +//=== ConstraintElimination.cpp - Eliminate based on constraints *- 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 +// +//===----------------------------------------------------------------------===// +// +// Eliminate conditions based on constraints collected from dominating +// conditions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Analysis/ConstraintSystem.h" +#include "llvm/Analysis/GlobalsModRef.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Dominators.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/PatternMatch.h" +#include "llvm/InitializePasses.h" +#include "llvm/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/DebugCounter.h" +#include "llvm/Transforms/Scalar.h" + +using namespace llvm; +using namespace PatternMatch; + +#define DEBUG_TYPE "constraint-elimination" + +STATISTIC(NumCondsRemoved, "Number of instructions removed"); +DEBUG_COUNTER(EliminatedCounter, "conds-eliminated", + "Controls which conditions are eliminated"); + +static int64_t MaxConstraintValue = 1 << 20; + +/// Turn a condition \p CmpI into a constraint vector, using indices from \p +/// Value2Index. If \p ShouldAdd is true, new indices are added for values not +/// yet in \p Value2Index. +static SmallVector +getConstraint(CmpInst *CmpI, DenseMap &Value2Index, + bool ShouldAdd) { + Value *A, *B; + ConstantInt *CI; + ICmpInst::Predicate Pred; + + auto IsOutOfRange = [](ConstantInt *CI) { + return CI->isNegative() || CI->uge(MaxConstraintValue); + }; + + auto TryToGetIndex = [ShouldAdd, + &Value2Index](Value *V) -> Optional { + if (ShouldAdd) { + Value2Index.insert({V, Value2Index.size() + 1}); + return Value2Index[V]; + } + auto I = Value2Index.find(V); + if (I == Value2Index.end()) + return None; + return I->second; + }; + + // ULE + if (match(CmpI, m_ICmp(Pred, m_Value(A), m_ConstantInt(CI))) && + Pred == CmpInst::ICMP_ULE) { + if (IsOutOfRange(CI)) + return {}; + + auto AIdx = TryToGetIndex(A); + if (!AIdx) + return {}; + + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = 1; + R[0] = CI->getSExtValue(); + return R; + } else if (match(CmpI, m_ICmp(Pred, m_ConstantInt(CI), m_Value(A))) && + Pred == CmpInst::ICMP_UGE) { + if (IsOutOfRange(CI)) + return {}; + + auto AIdx = TryToGetIndex(A); + if (!AIdx) + return {}; + + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = 1; + R[0] = CI->getSExtValue(); + return R; + } else if (match(CmpI, m_ICmp(Pred, m_Value(A), m_Value(B))) && + Pred == CmpInst::ICMP_ULE) { + auto AIdx = TryToGetIndex(A); + auto BIdx = TryToGetIndex(B); + if (!AIdx || !BIdx) + return {}; + + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = 1; + R[*BIdx] = -1; + return R; + } + + // ULT + if (match(CmpI, m_ICmp(Pred, m_Value(A), m_ConstantInt(CI))) && + Pred == CmpInst::ICMP_ULT) { + if (IsOutOfRange(CI)) + return {}; + + auto AIdx = TryToGetIndex(A); + if (!AIdx) + return {}; + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = 1; + R[0] = CI->getSExtValue() - 1; + return R; + } + + if (match(CmpI, m_ICmp(Pred, m_Value(A), m_Value(B))) && + Pred == CmpInst::ICMP_ULT) { + int64_t Offset = 0; + auto *GEP = dyn_cast(A); + if (GEP && isa(GEP->getOperand(GEP->getNumOperands() - 1))) { + A = GEP->getPointerOperand(); + Offset = + (-1) * cast(GEP->getOperand(GEP->getNumOperands() - 1)) + ->getSExtValue(); + } + auto AIdx = TryToGetIndex(A); + auto BIdx = TryToGetIndex(B); + if (!AIdx || !BIdx) + return {}; + + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = 1; + R[*BIdx] = -1; + R[0] = -1 + Offset; + return R; + } + + // UGE + if (match(CmpI, m_ICmp(Pred, m_Value(A), m_ConstantInt(CI))) && + Pred == CmpInst::ICMP_UGE) { + if (IsOutOfRange(CI)) + return {}; + + auto AIdx = TryToGetIndex(A); + if (!AIdx) + return {}; + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = -1; + R[0] = (-1) * CI->getSExtValue(); + return R; + } else if (match(CmpI, m_ICmp(Pred, m_ConstantInt(CI), m_Value(A))) && + Pred == CmpInst::ICMP_UGE) { + if (IsOutOfRange(CI)) + return {}; + + auto AIdx = TryToGetIndex(A); + if (!AIdx) + return {}; + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = -1; + R[0] = (-1) * CI->getSExtValue(); + return R; + } else if (match(CmpI, m_ICmp(Pred, m_Value(A), m_Value(B))) && + Pred == CmpInst::ICMP_UGE) { + auto AIdx = TryToGetIndex(A); + auto BIdx = TryToGetIndex(B); + if (!AIdx || !BIdx) + return {}; + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = -1; + R[*BIdx] = 1; + return R; + } + + // UGT + if (match(CmpI, m_ICmp(Pred, m_Value(A), m_Value(B))) && + Pred == CmpInst::ICMP_UGT) { + auto *C = dyn_cast(B); + if (C && IsOutOfRange(C)) + return {}; + + int64_t Offset = 0; + auto *GEP = dyn_cast(A); + if (GEP && isa(GEP->getOperand(GEP->getNumOperands() - 1))) { + A = GEP->getPointerOperand(); + Offset = cast(GEP->getOperand(GEP->getNumOperands() - 1)) + ->getSExtValue(); + } + + auto AIdx = TryToGetIndex(A); + if (!AIdx) + return {}; + + if (!C) { + auto BIdx = TryToGetIndex(B); + if (!BIdx) + return {}; + } + + SmallVector R(Value2Index.size() + 1, 0); + R[*AIdx] = -1; + R[0] = (C ? (-1) * C->getSExtValue() : 0) - 1 + Offset; + if (!C) + R[Value2Index[B]] = 1; + return R; + } + + return {}; +} + +/// Represents either a condition that holds on entry to a block or a basic +/// block, with their respective Dominator DFS in and out numbers. +struct ConstraintOrBlock { + unsigned NumIn; + unsigned NumOut; + bool IsBlock; + bool Not; + union { + BasicBlock *BB; + CmpInst *Condition; + }; + + ConstraintOrBlock(DomTreeNode *DTN) + : NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), IsBlock(true), + BB(DTN->getBlock()) {} + ConstraintOrBlock(DomTreeNode *DTN, CmpInst *Condition, bool Not) + : NumIn(DTN->getDFSNumIn()), NumOut(DTN->getDFSNumOut()), IsBlock(false), + Not(Not), Condition(Condition) {} +}; + +struct StackEntry { + unsigned NumIn; + unsigned NumOut; + CmpInst *Condition; + bool IsNot; + + StackEntry(unsigned NumIn, unsigned NumOut, CmpInst *Condition, bool IsNot) + : NumIn(NumIn), NumOut(NumOut), Condition(Condition), IsNot(IsNot) {} +}; + +static bool eliminateConstraints(Function &F, DominatorTree &DT) { + bool Changed = false; + DT.updateDFSNumbers(); + ConstraintSystem CS; + + SmallVector WorkList; + + // First, collect conditions implied by branches and blocks with their + // Dominator DFS in and out numbers. + for (BasicBlock &BB : F) { + if (!DT.getNode(&BB)) + continue; + WorkList.emplace_back(DT.getNode(&BB)); + + auto *Br = dyn_cast(BB.getTerminator()); + if (!Br || !Br->isConditional()) + continue; + auto *CmpI = dyn_cast(Br->getCondition()); + if (!CmpI) + continue; + if (Br->getSuccessor(0)->getSinglePredecessor()) + WorkList.emplace_back(DT.getNode(Br->getSuccessor(0)), CmpI, false); + if (Br->getSuccessor(1)->getSinglePredecessor()) + WorkList.emplace_back(DT.getNode(Br->getSuccessor(1)), CmpI, true); + } + + // Next, sort worklist by dominance, so that dominating blocks and conditions + // come before blocks and conditions dominated by them. If a block and a + // condition have the same numbers, the conditions comes before the block, as + // it holds on entry to the block. + sort(WorkList.begin(), WorkList.end(), + [](const ConstraintOrBlock &A, const ConstraintOrBlock &B) { + return std::tie(A.NumIn, A.IsBlock) < std::tie(B.NumIn, B.IsBlock); + }); + + // Finally, process ordered worklist and eliminate implied conditions. + SmallVector DFSInStack; + DenseMap Value2Index; + for (ConstraintOrBlock &CB : WorkList) { + // First, pop entries from the stack that are out-of-scope for CB. Remove + // the corresponding entry from the constraint system. + while (!DFSInStack.empty()) { + auto &E = DFSInStack.back(); + LLVM_DEBUG(dbgs() << "Top of stack : " << E.NumIn << " " << E.NumOut + << "\n"); + LLVM_DEBUG(dbgs() << "CB: " << CB.NumIn << " " << CB.NumOut << "\n"); + bool IsDom = CB.NumIn >= E.NumIn && CB.NumOut <= E.NumOut; + if (IsDom) + break; + LLVM_DEBUG(dbgs() << "Removing " << *E.Condition << " " << E.IsNot + << "\n"); + DFSInStack.pop_back(); + CS.popLastConstraint(); + } + + LLVM_DEBUG({ + dbgs() << "Processing "; + if (CB.IsBlock) + dbgs() << *CB.BB; + else + dbgs() << *CB.Condition; + dbgs() << "\n"; + }); + + // For a block, check if any CmpInsts become known based on the current set + // of constraints. + if (CB.IsBlock) { + for (Instruction &I : *CB.BB) { + auto *Cmp = dyn_cast(&I); + if (!Cmp) + continue; + auto R = getConstraint(Cmp, Value2Index, false); + if (R.empty()) + continue; + if (CS.isConditionImplied(R)) { + if (!DebugCounter::shouldExecute(EliminatedCounter)) + continue; + + LLVM_DEBUG(dbgs() << "Condition " << *Cmp + << " implied by dominating constraints\n"); + LLVM_DEBUG({ + for (auto &E : reverse(DFSInStack)) + dbgs() << " C " << *E.Condition << " " << E.IsNot << "\n"; + }); + Cmp->replaceAllUsesWith( + ConstantInt::getTrue(F.getParent()->getContext())); + NumCondsRemoved++; + Changed = true; + } + if (CS.isConditionImplied(ConstraintSystem::negate(R))) { + if (!DebugCounter::shouldExecute(EliminatedCounter)) + continue; + + LLVM_DEBUG(dbgs() << "Condition !" << *Cmp + << " implied by dominating constraints\n"); + LLVM_DEBUG({ + for (auto &E : reverse(DFSInStack)) + dbgs() << " C " << *E.Condition << " " << E.IsNot << "\n"; + }); + Cmp->replaceAllUsesWith( + ConstantInt::getFalse(F.getParent()->getContext())); + NumCondsRemoved++; + Changed = true; + } + } + continue; + } + + // Otherwise, add the condition to the system and stack, if we can transform + // it into a constraint. + auto R = getConstraint(CB.Condition, Value2Index, true); + if (R.empty()) + continue; + + LLVM_DEBUG(dbgs() << "Adding " << *CB.Condition << " " << CB.Not << "\n"); + if (CB.Not) + R = ConstraintSystem::negate(R); + + CS.addVariableRowFill(R); + DFSInStack.emplace_back(CB.NumIn, CB.NumOut, CB.Condition, CB.Not); + } + + return Changed; +} + +namespace { + +class ConstraintElimination : public FunctionPass { +public: + static char ID; + + ConstraintElimination() : FunctionPass(ID) { + initializeConstraintEliminationPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override { + auto &DT = getAnalysis().getDomTree(); + return eliminateConstraints(F, DT); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + } +}; + +} // end anonymous namespace + +char ConstraintElimination::ID = 0; + +INITIALIZE_PASS_BEGIN(ConstraintElimination, "constraint-elimination", + "Constraint Elimination", false, false) +INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass) +INITIALIZE_PASS_DEPENDENCY(LazyValueInfoWrapperPass) +INITIALIZE_PASS_END(ConstraintElimination, "constraint-elimination", + "Constraint Elimination", false, false) + +FunctionPass *llvm::createConstraintEliminationPass() { + return new ConstraintElimination(); +} diff --git a/llvm/lib/Transforms/Scalar/Scalar.cpp b/llvm/lib/Transforms/Scalar/Scalar.cpp --- a/llvm/lib/Transforms/Scalar/Scalar.cpp +++ b/llvm/lib/Transforms/Scalar/Scalar.cpp @@ -39,6 +39,7 @@ initializeCallSiteSplittingLegacyPassPass(Registry); initializeConstantHoistingLegacyPassPass(Registry); initializeConstantPropagationPass(Registry); + initializeConstraintEliminationPass(Registry); initializeCorrelatedValuePropagationPass(Registry); initializeDCELegacyPassPass(Registry); initializeDeadInstEliminationPass(Registry); diff --git a/llvm/test/CodeGen/AMDGPU/opt-pipeline.ll b/llvm/test/CodeGen/AMDGPU/opt-pipeline.ll --- a/llvm/test/CodeGen/AMDGPU/opt-pipeline.ll +++ b/llvm/test/CodeGen/AMDGPU/opt-pipeline.ll @@ -108,6 +108,7 @@ ; GCN-O1-NEXT: Function Alias Analysis Results ; GCN-O1-NEXT: Memory SSA ; GCN-O1-NEXT: Early CSE w/ MemorySSA +; GCN-O1-NEXT: Constraint Elimination ; GCN-O1-NEXT: Simplify the CFG ; GCN-O1-NEXT: Dominator Tree Construction ; GCN-O1-NEXT: Basic Alias Analysis (stateless AA impl) @@ -414,6 +415,7 @@ ; GCN-O2-NEXT: Function Alias Analysis Results ; GCN-O2-NEXT: Memory SSA ; GCN-O2-NEXT: Early CSE w/ MemorySSA +; GCN-O2-NEXT: Constraint Elimination ; GCN-O2-NEXT: Speculatively execute instructions if target has divergent branches ; GCN-O2-NEXT: Function Alias Analysis Results ; GCN-O2-NEXT: Lazy Value Information Analysis @@ -771,6 +773,7 @@ ; GCN-O3-NEXT: Function Alias Analysis Results ; GCN-O3-NEXT: Memory SSA ; GCN-O3-NEXT: Early CSE w/ MemorySSA +; GCN-O3-NEXT: Constraint Elimination ; GCN-O3-NEXT: Speculatively execute instructions if target has divergent branches ; GCN-O3-NEXT: Function Alias Analysis Results ; GCN-O3-NEXT: Lazy Value Information Analysis diff --git a/llvm/test/Other/opt-O2-pipeline.ll b/llvm/test/Other/opt-O2-pipeline.ll --- a/llvm/test/Other/opt-O2-pipeline.ll +++ b/llvm/test/Other/opt-O2-pipeline.ll @@ -68,6 +68,7 @@ ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: Memory SSA ; CHECK-NEXT: Early CSE w/ MemorySSA +; CHECK-NEXT: Constraint Elimination ; CHECK-NEXT: Speculatively execute instructions if target has divergent branches ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: Lazy Value Information Analysis diff --git a/llvm/test/Other/opt-O3-pipeline.ll b/llvm/test/Other/opt-O3-pipeline.ll --- a/llvm/test/Other/opt-O3-pipeline.ll +++ b/llvm/test/Other/opt-O3-pipeline.ll @@ -72,6 +72,7 @@ ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: Memory SSA ; CHECK-NEXT: Early CSE w/ MemorySSA +; CHECK-NEXT: Constraint Elimination ; CHECK-NEXT: Speculatively execute instructions if target has divergent branches ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: Lazy Value Information Analysis diff --git a/llvm/test/Other/opt-Os-pipeline.ll b/llvm/test/Other/opt-Os-pipeline.ll --- a/llvm/test/Other/opt-Os-pipeline.ll +++ b/llvm/test/Other/opt-Os-pipeline.ll @@ -68,6 +68,7 @@ ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: Memory SSA ; CHECK-NEXT: Early CSE w/ MemorySSA +; CHECK-NEXT: Constraint Elimination ; CHECK-NEXT: Speculatively execute instructions if target has divergent branches ; CHECK-NEXT: Function Alias Analysis Results ; CHECK-NEXT: Lazy Value Information Analysis diff --git a/llvm/test/Transforms/ConstraintElimination/dom.ll b/llvm/test/Transforms/ConstraintElimination/dom.ll --- a/llvm/test/Transforms/ConstraintElimination/dom.ll +++ b/llvm/test/Transforms/ConstraintElimination/dom.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s ; Test cases where both the true and false successors reach the same block, ; dominated by one of them. @@ -13,7 +13,7 @@ ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: ; CHECK-NEXT: [[C_2:%.*]] = icmp ule i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: br label [[BB2]] ; CHECK: bb2: ; CHECK-NEXT: [[C_3:%.*]] = icmp ugt i32 [[X]], 10 @@ -47,7 +47,7 @@ ; CHECK-NEXT: ret i32 20 ; CHECK: bb2: ; CHECK-NEXT: [[C_3:%.*]] = icmp ule i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[C_3]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: br label [[BB1]] ; entry: @@ -80,7 +80,7 @@ ; CHECK-NEXT: ret i32 10 ; CHECK: bb2: ; CHECK-NEXT: [[C_3:%.*]] = icmp ugt i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[C_3]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: ret i32 20 ; entry: @@ -110,7 +110,7 @@ ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2]] ; CHECK: bb1: ; CHECK-NEXT: [[C_2:%.*]] = icmp ule i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[C_2]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: ret i32 10 ; CHECK: bb2: ; CHECK-NEXT: [[C_3:%.*]] = icmp ugt i32 [[X]], 10 diff --git a/llvm/test/Transforms/ConstraintElimination/geps.ll b/llvm/test/Transforms/ConstraintElimination/geps.ll --- a/llvm/test/Transforms/ConstraintElimination/geps.ll +++ b/llvm/test/Transforms/ConstraintElimination/geps.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s define i32 @test(i32* readonly %src, i32* readnone %min, i32* readnone %max) { ; CHECK-LABEL: @test( @@ -16,7 +16,7 @@ ; CHECK-NEXT: [[L0:%.*]] = load i32, i32* [[SRC]], align 4 ; CHECK-NEXT: [[ADD_PTR_I36:%.*]] = getelementptr inbounds i32, i32* [[SRC]], i64 3 ; CHECK-NEXT: [[C_3_MIN:%.*]] = icmp ult i32* [[ADD_PTR_I36]], [[MIN]] -; CHECK-NEXT: br i1 [[C_3_MIN]], label [[TRAP]], label [[CHECK_3_MAX:%.*]] +; CHECK-NEXT: br i1 false, label [[TRAP]], label [[CHECK_3_MAX:%.*]] ; CHECK: check.3.max: ; CHECK-NEXT: [[C_3_MAX:%.*]] = icmp ult i32* [[ADD_PTR_I36]], [[MAX]] ; CHECK-NEXT: br i1 [[C_3_MAX]], label [[CHECK_1_MIN:%.*]], label [[TRAP]] @@ -24,18 +24,18 @@ ; CHECK-NEXT: [[L1:%.*]] = load i32, i32* [[ADD_PTR_I36]], align 4 ; CHECK-NEXT: [[ADD_PTR_I29:%.*]] = getelementptr inbounds i32, i32* [[SRC]], i64 1 ; CHECK-NEXT: [[C_1_MIN:%.*]] = icmp ult i32* [[ADD_PTR_I29]], [[MIN]] -; CHECK-NEXT: br i1 [[C_1_MIN]], label [[TRAP]], label [[CHECK_1_MAX:%.*]] +; CHECK-NEXT: br i1 false, label [[TRAP]], label [[CHECK_1_MAX:%.*]] ; CHECK: check.1.max: ; CHECK-NEXT: [[C_1_MAX:%.*]] = icmp ult i32* [[ADD_PTR_I29]], [[MAX]] -; CHECK-NEXT: br i1 [[C_1_MAX]], label [[CHECK_2_MIN:%.*]], label [[TRAP]] +; CHECK-NEXT: br i1 true, label [[CHECK_2_MIN:%.*]], label [[TRAP]] ; CHECK: check.2.min: ; CHECK-NEXT: [[L2:%.*]] = load i32, i32* [[ADD_PTR_I29]], align 4 ; CHECK-NEXT: [[ADD_PTR_I:%.*]] = getelementptr inbounds i32, i32* [[SRC]], i64 2 ; CHECK-NEXT: [[C_2_MIN:%.*]] = icmp ult i32* [[ADD_PTR_I]], [[MIN]] -; CHECK-NEXT: br i1 [[C_2_MIN]], label [[TRAP]], label [[CHECK_2_MAX:%.*]] +; CHECK-NEXT: br i1 false, label [[TRAP]], label [[CHECK_2_MAX:%.*]] ; CHECK: check.2.max: ; CHECK-NEXT: [[C_2_MAX:%.*]] = icmp ult i32* [[ADD_PTR_I]], [[MAX]] -; CHECK-NEXT: br i1 [[C_2_MAX]], label [[EXIT:%.*]], label [[TRAP]] +; CHECK-NEXT: br i1 true, label [[EXIT:%.*]], label [[TRAP]] ; CHECK: exit: ; CHECK-NEXT: [[L3:%.*]] = load i32, i32* [[ADD_PTR_I]], align 4 ; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[L1]], [[L0]] diff --git a/llvm/test/Transforms/ConstraintElimination/i128.ll b/llvm/test/Transforms/ConstraintElimination/i128.ll --- a/llvm/test/Transforms/ConstraintElimination/i128.ll +++ b/llvm/test/Transforms/ConstraintElimination/i128.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s declare void @use(i1) diff --git a/llvm/test/Transforms/ConstraintElimination/loops.ll b/llvm/test/Transforms/ConstraintElimination/loops.ll --- a/llvm/test/Transforms/ConstraintElimination/loops.ll +++ b/llvm/test/Transforms/ConstraintElimination/loops.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s ; Make sure conditions in loops are not used to simplify themselves. diff --git a/llvm/test/Transforms/ConstraintElimination/mixed.ll b/llvm/test/Transforms/ConstraintElimination/mixed.ll --- a/llvm/test/Transforms/ConstraintElimination/mixed.ll +++ b/llvm/test/Transforms/ConstraintElimination/mixed.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s ; Make sure we do not incorrectly add variables to the system. diff --git a/llvm/test/Transforms/ConstraintElimination/uge.ll b/llvm/test/Transforms/ConstraintElimination/uge.ll --- a/llvm/test/Transforms/ConstraintElimination/uge.ll +++ b/llvm/test/Transforms/ConstraintElimination/uge.ll @@ -1,31 +1,28 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -instcombine -S %s | FileCheck %s declare void @use(i1) define void @test_1_variable_constraint(i32 %x, i32 %y, i32 %z) { ; CHECK-LABEL: @test_1_variable_constraint( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] +; CHECK-NEXT: [[C_1_NOT:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: br i1 [[C_1_NOT]], label [[BB2:%.*]], label [[BB1:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[T_1:%.*]] = icmp uge i32 [[X]], [[Y]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[C_2:%.*]] = icmp uge i32 [[X]], 10 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[X]], 9 ; CHECK-NEXT: call void @use(i1 [[C_2]]) ; CHECK-NEXT: [[C_3:%.*]] = icmp uge i32 [[Y]], [[X]] ; CHECK-NEXT: call void @use(i1 [[C_3]]) -; CHECK-NEXT: [[C_4:%.*]] = icmp uge i32 10, [[X]] +; CHECK-NEXT: [[C_4:%.*]] = icmp ult i32 [[X]], 11 ; CHECK-NEXT: call void @use(i1 [[C_4]]) ; CHECK-NEXT: ret void ; CHECK: bb2: -; CHECK-NEXT: [[T_2:%.*]] = icmp uge i32 [[Y]], [[X]] -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[F_1:%.*]] = icmp uge i32 [[X]], [[Y]] -; CHECK-NEXT: call void @use(i1 [[F_1]]) -; CHECK-NEXT: [[C_5:%.*]] = icmp uge i32 [[X]], 10 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: [[C_5:%.*]] = icmp ugt i32 [[X]], 9 ; CHECK-NEXT: call void @use(i1 [[C_5]]) -; CHECK-NEXT: [[C_6:%.*]] = icmp uge i32 10, [[X]] +; CHECK-NEXT: [[C_6:%.*]] = icmp ult i32 [[X]], 11 ; CHECK-NEXT: call void @use(i1 [[C_6]]) ; CHECK-NEXT: ret void ; @@ -59,28 +56,23 @@ define void @test_1_constant_constraint(i32 %x) { ; CHECK-LABEL: @test_1_constant_constraint( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[X:%.*]], 10 +; CHECK-NEXT: [[C_1:%.*]] = icmp ugt i32 [[X:%.*]], 9 ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[T_1:%.*]] = icmp uge i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[T_1]]) -; CHECK-NEXT: [[T_2:%.*]] = icmp uge i32 [[X]], 9 -; CHECK-NEXT: call void @use(i1 [[T_2]]) -; CHECK-NEXT: [[C_2:%.*]] = icmp uge i32 [[X]], 11 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: [[C_2:%.*]] = icmp ne i32 [[X]], 10 ; CHECK-NEXT: call void @use(i1 [[C_2]]) -; CHECK-NEXT: [[C_4:%.*]] = icmp uge i32 10, [[X]] +; CHECK-NEXT: [[C_4:%.*]] = icmp eq i32 [[X]], 10 ; CHECK-NEXT: call void @use(i1 [[C_4]]) ; CHECK-NEXT: ret void ; CHECK: bb2: -; CHECK-NEXT: [[T_3:%.*]] = icmp uge i32 11, [[X]] -; CHECK-NEXT: call void @use(i1 [[T_3]]) -; CHECK-NEXT: [[F_1:%.*]] = icmp uge i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[F_1]]) -; CHECK-NEXT: [[F_1_1:%.*]] = icmp uge i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[F_1_1]]) -; CHECK-NEXT: [[C_5:%.*]] = icmp uge i32 [[X]], 9 +; CHECK-NEXT: call void @use(i1 true) +; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: call void @use(i1 false) +; CHECK-NEXT: [[C_5:%.*]] = icmp eq i32 [[X]], 9 ; CHECK-NEXT: call void @use(i1 [[C_5]]) -; CHECK-NEXT: [[C_6:%.*]] = icmp uge i32 1, [[X]] +; CHECK-NEXT: [[C_6:%.*]] = icmp ult i32 [[X]], 2 ; CHECK-NEXT: call void @use(i1 [[C_6]]) ; CHECK-NEXT: ret void ; @@ -118,14 +110,14 @@ define i32 @test1(i32 %x, i32 %y, i32 %z) { ; CHECK-LABEL: @test1( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] +; CHECK-NEXT: [[C_1_NOT:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: br i1 [[C_1_NOT]], label [[EXIT:%.*]], label [[BB1:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp uge i32 [[Y]], [[Z:%.*]] -; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] +; CHECK-NEXT: [[C_2_NOT:%.*]] = icmp ult i32 [[Y]], [[Z:%.*]] +; CHECK-NEXT: br i1 [[C_2_NOT]], label [[EXIT]], label [[BB2:%.*]] ; CHECK: bb2: -; CHECK-NEXT: [[C_3:%.*]] = icmp uge i32 [[X]], [[Z]] -; CHECK-NEXT: br i1 [[C_3]], label [[BB3:%.*]], label [[EXIT]] +; CHECK-NEXT: [[C_3_NOT:%.*]] = icmp ult i32 [[X]], [[Z]] +; CHECK-NEXT: br i1 [[C_3_NOT]], label [[EXIT]], label [[BB3:%.*]] ; CHECK: bb3: ; CHECK-NEXT: ret i32 10 ; CHECK: exit: @@ -154,14 +146,14 @@ define i32 @test2(i32 %x, i32 %y, i32 %z, i32 %a) { ; CHECK-LABEL: @test2( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] +; CHECK-NEXT: [[C_1_NOT:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: br i1 [[C_1_NOT]], label [[EXIT:%.*]], label [[BB1:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp uge i32 [[Y]], [[Z:%.*]] -; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] +; CHECK-NEXT: [[C_2_NOT:%.*]] = icmp ult i32 [[Y]], [[Z:%.*]] +; CHECK-NEXT: br i1 [[C_2_NOT]], label [[EXIT]], label [[BB2:%.*]] ; CHECK: bb2: -; CHECK-NEXT: [[C_3:%.*]] = icmp uge i32 [[X]], [[A:%.*]] -; CHECK-NEXT: br i1 [[C_3]], label [[BB3:%.*]], label [[EXIT]] +; CHECK-NEXT: [[C_3_NOT:%.*]] = icmp ult i32 [[X]], [[A:%.*]] +; CHECK-NEXT: br i1 [[C_3_NOT]], label [[EXIT]], label [[BB3:%.*]] ; CHECK: bb3: ; CHECK-NEXT: ret i32 10 ; CHECK: exit: @@ -190,10 +182,10 @@ define i32 @test3(i32 %x, i32 %y) { ; CHECK-LABEL: @test3( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[X:%.*]], 10 +; CHECK-NEXT: [[C_1:%.*]] = icmp ugt i32 [[X:%.*]], 9 ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp uge i32 [[Y:%.*]], 20 +; CHECK-NEXT: [[C_2:%.*]] = icmp ugt i32 [[Y:%.*]], 19 ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: ; CHECK-NEXT: ret i32 10 @@ -218,11 +210,11 @@ define i32 @test4(i32 %x, i32 %y, i32 %z) { ; CHECK-LABEL: @test4( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[C_1:%.*]] = icmp uge i32 [[X:%.*]], [[Y:%.*]] -; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[EXIT:%.*]] +; CHECK-NEXT: [[C_1_NOT:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: br i1 [[C_1_NOT]], label [[EXIT:%.*]], label [[BB1:%.*]] ; CHECK: bb1: -; CHECK-NEXT: [[C_2:%.*]] = icmp uge i32 [[Y]], [[Z:%.*]] -; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] +; CHECK-NEXT: [[C_2_NOT:%.*]] = icmp ult i32 [[Y]], [[Z:%.*]] +; CHECK-NEXT: br i1 [[C_2_NOT]], label [[EXIT]], label [[BB2:%.*]] ; CHECK: bb2: ; CHECK-NEXT: [[T_1:%.*]] = icmp uge i32 [[X]], [[Z]] ; CHECK-NEXT: call void @use(i1 [[T_1]]) diff --git a/llvm/test/Transforms/ConstraintElimination/ugt-ule.ll b/llvm/test/Transforms/ConstraintElimination/ugt-ule.ll --- a/llvm/test/Transforms/ConstraintElimination/ugt-ule.ll +++ b/llvm/test/Transforms/ConstraintElimination/ugt-ule.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s declare void @use(i1) @@ -10,13 +10,13 @@ ; CHECK-NEXT: br i1 [[CMP_1]], label [[BB_1:%.*]], label [[BB_2:%.*]] ; CHECK: bb.1: ; CHECK-NEXT: [[CMP_2:%.*]] = icmp uge i8* [[M]], [[PTR]] -; CHECK-NEXT: call void @use(i1 [[CMP_2]]) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: ret void ; CHECK: bb.2: ; CHECK-NEXT: br label [[BB_2_NEXT:%.*]] ; CHECK: bb.2.next: ; CHECK-NEXT: [[CMP_3:%.*]] = icmp uge i8* [[M]], [[PTR]] -; CHECK-NEXT: call void @use(i1 [[CMP_3]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: ret void ; entry: diff --git a/llvm/test/Transforms/ConstraintElimination/ule.ll b/llvm/test/Transforms/ConstraintElimination/ule.ll --- a/llvm/test/Transforms/ConstraintElimination/ule.ll +++ b/llvm/test/Transforms/ConstraintElimination/ule.ll @@ -1,5 +1,5 @@ ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; RUN: opt -S %s | FileCheck %s +; RUN: opt -constraint-elimination -S %s | FileCheck %s declare void @use(i1) @@ -10,7 +10,7 @@ ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: ; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[X]], [[Y]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[C_2:%.*]] = icmp ule i32 [[X]], 10 ; CHECK-NEXT: call void @use(i1 [[C_2]]) ; CHECK-NEXT: [[C_3:%.*]] = icmp ule i32 [[Y]], [[X]] @@ -20,9 +20,9 @@ ; CHECK-NEXT: ret void ; CHECK: bb2: ; CHECK-NEXT: [[T_2:%.*]] = icmp ule i32 [[Y]], [[X]] -; CHECK-NEXT: call void @use(i1 [[T_2]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[F_1:%.*]] = icmp ule i32 [[X]], [[Y]] -; CHECK-NEXT: call void @use(i1 [[F_1]]) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[C_5:%.*]] = icmp ule i32 [[X]], 10 ; CHECK-NEXT: call void @use(i1 [[C_5]]) ; CHECK-NEXT: [[C_6:%.*]] = icmp ule i32 10, [[X]] @@ -63,9 +63,9 @@ ; CHECK-NEXT: br i1 [[C_1]], label [[BB1:%.*]], label [[BB2:%.*]] ; CHECK: bb1: ; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[T_1]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[T_2:%.*]] = icmp ule i32 [[X]], 11 -; CHECK-NEXT: call void @use(i1 [[T_2]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[C_2:%.*]] = icmp ule i32 [[X]], 9 ; CHECK-NEXT: call void @use(i1 [[C_2]]) ; CHECK-NEXT: [[C_4:%.*]] = icmp ule i32 10, [[X]] @@ -75,9 +75,9 @@ ; CHECK-NEXT: [[T_3:%.*]] = icmp ule i32 10, [[X]] ; CHECK-NEXT: call void @use(i1 [[T_3]]) ; CHECK-NEXT: [[F_1:%.*]] = icmp ule i32 [[X]], 9 -; CHECK-NEXT: call void @use(i1 [[F_1]]) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[F_1_1:%.*]] = icmp ule i32 [[X]], 10 -; CHECK-NEXT: call void @use(i1 [[F_1_1]]) +; CHECK-NEXT: call void @use(i1 false) ; CHECK-NEXT: [[C_5:%.*]] = icmp ule i32 [[X]], 11 ; CHECK-NEXT: call void @use(i1 [[C_5]]) ; CHECK-NEXT: [[C_6:%.*]] = icmp ule i32 10, [[X]] @@ -126,7 +126,7 @@ ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: ; CHECK-NEXT: [[C_3:%.*]] = icmp ule i32 [[X]], [[Z]] -; CHECK-NEXT: br i1 [[C_3]], label [[BB3:%.*]], label [[EXIT]] +; CHECK-NEXT: br i1 true, label [[BB3:%.*]], label [[EXIT]] ; CHECK: bb3: ; CHECK-NEXT: ret i32 10 ; CHECK: exit: @@ -226,7 +226,7 @@ ; CHECK-NEXT: br i1 [[C_2]], label [[BB2:%.*]], label [[EXIT]] ; CHECK: bb2: ; CHECK-NEXT: [[T_1:%.*]] = icmp ule i32 [[X]], [[Z]] -; CHECK-NEXT: call void @use(i1 [[T_1]]) +; CHECK-NEXT: call void @use(i1 true) ; CHECK-NEXT: [[U_1:%.*]] = icmp eq i32 [[X]], [[Z]] ; CHECK-NEXT: call void @use(i1 [[U_1]]) ; CHECK-NEXT: ret i32 10