diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index 9a743b95a2a6..fc5ee2359a76 100644 --- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp +++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp @@ -1223,6 +1223,16 @@ void State::addInfoFor(BasicBlock &BB) { break; } + // Add facts from unsigned division and remainder. + // urem x, n: result < n and result <= x + // udiv x, n: result <= x + if (auto *BO = dyn_cast(&I)) { + if ((BO->getOpcode() == Instruction::URem || + BO->getOpcode() == Instruction::UDiv) && + isGuaranteedNotToBePoison(BO)) + WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), BO)); + } + GuaranteedToExecute &= isGuaranteedToTransferExecutionToSuccessor(&I); } @@ -2000,6 +2010,21 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI, continue; } + if (auto *BO = dyn_cast(CB.Inst)) { + if (BO->getOpcode() == Instruction::URem) { + // urem x, n: result < n (remainder is always less than divisor) + AddFact(CmpInst::ICMP_ULT, BO, BO->getOperand(1)); + // urem x, n: result <= x (remainder is at most the dividend) + AddFact(CmpInst::ICMP_ULE, BO, BO->getOperand(0)); + continue; + } + if (BO->getOpcode() == Instruction::UDiv) { + // udiv x, n: result <= x (quotient is at most the dividend) + AddFact(CmpInst::ICMP_ULE, BO, BO->getOperand(0)); + continue; + } + } + auto &DL = F.getDataLayout(); auto AddFactsAboutIndices = [&](Value *Ptr, Type *AccessType) { CmpPredicate Pred; diff --git a/llvm/test/Transforms/ConstraintElimination/urem-udiv.ll b/llvm/test/Transforms/ConstraintElimination/urem-udiv.ll new file mode 100644 index 000000000000..c9c326032661 --- /dev/null +++ b/llvm/test/Transforms/ConstraintElimination/urem-udiv.ll @@ -0,0 +1,361 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 4 +; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s + +; n < m /\ r = urem x, n ==> r < n < m ==> r < m +define i1 @urem_ult_divisor_bound(i32 noundef %x, i32 noundef %n, i32 noundef %m) { +; CHECK-LABEL: define i1 @urem_ult_divisor_bound( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[M:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %n, %m + br i1 %cmp, label %mid, label %else + +mid: + %r = urem i32 %x, %n + br label %check + +check: + %v = icmp ult i32 %r, %m + ret i1 %v + +else: + ret i1 false +} + +; n <= m /\ r = urem x, n ==> r < n <= m ==> r ule m +define i1 @urem_ule_divisor_bound(i32 noundef %x, i32 noundef %n, i32 noundef %m) { +; CHECK-LABEL: define i1 @urem_ule_divisor_bound( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[M:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ule i32 %n, %m + br i1 %cmp, label %mid, label %else + +mid: + %r = urem i32 %x, %n + br label %check + +check: + %v = icmp ule i32 %r, %m + ret i1 %v + +else: + ret i1 false +} + +; x <= limit /\ r = urem x, n ==> r <= x <= limit +define i1 @urem_ule_dividend_bound(i32 noundef %x, i32 noundef %n, i32 noundef %limit) { +; CHECK-LABEL: define i1 @urem_ule_dividend_bound( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[LIMIT:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[X]], [[LIMIT]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ule i32 %x, %limit + br i1 %cmp, label %mid, label %else + +mid: + %r = urem i32 %x, %n + br label %check + +check: + %v = icmp ule i32 %r, %limit + ret i1 %v + +else: + ret i1 false +} + +; x <= limit /\ q = udiv x, n ==> q <= x <= limit +define i1 @udiv_ule_dividend_bound(i32 noundef %x, i32 noundef %n, i32 noundef %limit) { +; CHECK-LABEL: define i1 @udiv_ule_dividend_bound( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[LIMIT:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[X]], [[LIMIT]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[Q:%.*]] = udiv i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ule i32 %x, %limit + br i1 %cmp, label %mid, label %else + +mid: + %q = udiv i32 %x, %n + br label %check + +check: + %v = icmp ule i32 %q, %limit + ret i1 %v + +else: + ret i1 false +} + +; n < m /\ m < k /\ r = urem x, n ==> r < n < m < k ==> r < k +define i1 @urem_chain_three_hops(i32 noundef %x, i32 noundef %n, i32 noundef %m, i32 noundef %k) { +; CHECK-LABEL: define i1 @urem_chain_three_hops( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[M:%.*]], i32 noundef [[K:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[C1:%.*]] = icmp ult i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[C1]], label [[BB1:%.*]], label [[OUT:%.*]] +; CHECK: bb1: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: [[C2:%.*]] = icmp ult i32 [[M]], [[K]] +; CHECK-NEXT: br i1 [[C2]], label [[BB2:%.*]], label [[OUT]] +; CHECK: bb2: +; CHECK-NEXT: ret i1 true +; CHECK: out: +; CHECK-NEXT: ret i1 false +; +entry: + %c1 = icmp ult i32 %n, %m + br i1 %c1, label %bb1, label %out + +bb1: + %r = urem i32 %x, %n + %c2 = icmp ult i32 %m, %k + br i1 %c2, label %bb2, label %out + +bb2: + %v = icmp ult i32 %r, %k + ret i1 %v + +out: + ret i1 false +} + +define i1 @udiv_urem_combined(i32 noundef %x, i32 noundef %n, i32 noundef %limit) { +; CHECK-LABEL: define i1 @udiv_urem_combined( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[LIMIT:%.*]]) { +; CHECK-NEXT: [[CMP1:%.*]] = icmp ult i32 [[N]], [[LIMIT]] +; CHECK-NEXT: [[CMP2:%.*]] = icmp ule i32 [[X]], [[LIMIT]] +; CHECK-NEXT: [[AND:%.*]] = and i1 [[CMP1]], [[CMP2]] +; CHECK-NEXT: br i1 [[AND]], label [[THEN:%.*]], label [[ELSE:%.*]] +; CHECK: then: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: [[Q:%.*]] = udiv i32 [[X]], [[N]] +; CHECK-NEXT: [[RES:%.*]] = and i1 true, true +; CHECK-NEXT: ret i1 [[RES]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; + %cmp1 = icmp ult i32 %n, %limit + %cmp2 = icmp ule i32 %x, %limit + %and = and i1 %cmp1, %cmp2 + br i1 %and, label %then, label %else + +then: + %r = urem i32 %x, %n + %q = udiv i32 %x, %n + %cmp3 = icmp ule i32 %r, %limit + %cmp4 = icmp ule i32 %q, %limit + %res = and i1 %cmp3, %cmp4 + ret i1 %res + +else: + ret i1 false +} + +define i1 @urem_i64(i64 noundef %x, i64 noundef %n, i64 noundef %m) { +; CHECK-LABEL: define i1 @urem_i64( +; CHECK-SAME: i64 noundef [[X:%.*]], i64 noundef [[N:%.*]], i64 noundef [[M:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[N]], [[M]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = urem i64 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: ret i1 true +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i64 %n, %m + br i1 %cmp, label %mid, label %else + +mid: + %r = urem i64 %x, %n + br label %check + +check: + %v = icmp ult i64 %r, %m + ret i1 %v + +else: + ret i1 false +} + +; Without noundef, isGuaranteedNotToBePoison may fail; don't add facts. +define i1 @neg_urem_no_noundef(i32 %x, i32 %n, i32 %m) { +; CHECK-LABEL: define i1 @neg_urem_no_noundef( +; CHECK-SAME: i32 [[X:%.*]], i32 [[N:%.*]], i32 [[M:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: [[V:%.*]] = icmp ult i32 [[R]], [[M]] +; CHECK-NEXT: ret i1 [[V]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %n, %m + br i1 %cmp, label %mid, label %else + +mid: + %r = urem i32 %x, %n + br label %check + +check: + %v = icmp ult i32 %r, %m + ret i1 %v + +else: + ret i1 false +} + +define i1 @neg_udiv_no_noundef(i32 %x, i32 %n, i32 %limit) { +; CHECK-LABEL: define i1 @neg_udiv_no_noundef( +; CHECK-SAME: i32 [[X:%.*]], i32 [[N:%.*]], i32 [[LIMIT:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ule i32 [[X]], [[LIMIT]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[Q:%.*]] = udiv i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: [[V:%.*]] = icmp ule i32 [[Q]], [[LIMIT]] +; CHECK-NEXT: ret i1 [[V]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ule i32 %x, %limit + br i1 %cmp, label %mid, label %else + +mid: + %q = udiv i32 %x, %n + br label %check + +check: + %v = icmp ule i32 %q, %limit + ret i1 %v + +else: + ret i1 false +} + +; srem/sdiv are NOT handled. +define i1 @neg_srem_not_handled(i32 noundef %x, i32 noundef %n, i32 noundef %m) { +; CHECK-LABEL: define i1 @neg_srem_not_handled( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[M:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[N]], [[M]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = srem i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: [[V:%.*]] = icmp ult i32 [[R]], [[M]] +; CHECK-NEXT: ret i1 [[V]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %n, %m + br i1 %cmp, label %mid, label %else + +mid: + %r = srem i32 %x, %n + br label %check + +check: + %v = icmp ult i32 %r, %m + ret i1 %v + +else: + ret i1 false +} + +; No dominating constraint relating n to m; can't simplify. +define i1 @neg_no_dominating_fact(i32 noundef %x, i32 noundef %n, i32 noundef %m) { +; CHECK-LABEL: define i1 @neg_no_dominating_fact( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[M:%.*]]) { +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: [[V:%.*]] = icmp ult i32 [[R]], [[M]] +; CHECK-NEXT: ret i1 [[V]] +; + %r = urem i32 %x, %n + %v = icmp ult i32 %r, %m + ret i1 %v +} + +; Wrong direction: m < n, r < n, but r < m is NOT implied. +define i1 @neg_wrong_direction(i32 noundef %x, i32 noundef %n, i32 noundef %m) { +; CHECK-LABEL: define i1 @neg_wrong_direction( +; CHECK-SAME: i32 noundef [[X:%.*]], i32 noundef [[N:%.*]], i32 noundef [[M:%.*]]) { +; CHECK-NEXT: entry: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[M]], [[N]] +; CHECK-NEXT: br i1 [[CMP]], label [[MID:%.*]], label [[ELSE:%.*]] +; CHECK: mid: +; CHECK-NEXT: [[R:%.*]] = urem i32 [[X]], [[N]] +; CHECK-NEXT: br label [[CHECK:%.*]] +; CHECK: check: +; CHECK-NEXT: [[V:%.*]] = icmp ult i32 [[R]], [[M]] +; CHECK-NEXT: ret i1 [[V]] +; CHECK: else: +; CHECK-NEXT: ret i1 false +; +entry: + %cmp = icmp ult i32 %m, %n + br i1 %cmp, label %mid, label %else + +mid: + %r = urem i32 %x, %n + br label %check + +check: + %v = icmp ult i32 %r, %m + ret i1 %v + +else: + ret i1 false +}