-
Notifications
You must be signed in to change notification settings - Fork 12.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[InstSimplify] Fold X < Y ? (X + zext(X < Y)) <= Y : false
to X < Y
#118579
base: main
Are you sure you want to change the base?
Conversation
@llvm/pr-subscribers-llvm-analysis Author: Veera (veera-sivarajan) ChangesThis PR simplifies: Proof: https://alive2.llvm.org/ce/z/JFcDzM This helps improving codegen for Rust's loop over inclusive range with variable upper bound in rust-lang/rust#102462. Full diff: https://github.com/llvm/llvm-project/pull/118579.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 01b0a089aab718..8e4731651ea59f 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4639,6 +4639,45 @@ static Value *simplifySelectWithEquivalence(Value *CmpLHS, Value *CmpRHS,
return nullptr;
}
+/// Simplifies:
+/// `X > Y ? (X + zext(X > Y)) >= Y : false` to `X > Y`
+/// `X < Y ? (X + zext(X < Y)) <= Y : false` to `X < Y`
+static Value *simplifySelectWithStrictICmp(Value *CondVal, Value *TVal,
+ Value *FVal,
+ const SimplifyQuery &Q) {
+ if (!match(FVal, m_Zero()))
+ return nullptr;
+
+ ICmpInst::Predicate Pred;
+ Value *CmpLHS, *CmpRHS;
+
+ if (!match(CondVal, m_ICmp(Pred, m_Value(CmpLHS), m_Value(CmpRHS))))
+ return nullptr;
+
+ if (!CmpInst::isStrictPredicate(Pred))
+ return nullptr;
+
+ ICmpInst::Predicate NonStrictPred = ICmpInst::getNonStrictPredicate(Pred);
+ BinaryOperator *BinOp;
+
+ if (!match(TVal,
+ m_SpecificICmp(NonStrictPred, m_BinOp(BinOp), m_Specific(CmpRHS))))
+ return nullptr;
+
+ // This fold works for GT only when it does not wrap.
+ if (Pred == ICmpInst::ICMP_UGT && !Q.IIQ.hasNoUnsignedWrap(BinOp))
+ return nullptr;
+
+ if (Pred == ICmpInst::ICMP_SGT && !Q.IIQ.hasNoSignedWrap(BinOp))
+ return nullptr;
+
+ if (!match(BinOp, m_c_BinOp(Instruction::Add, m_Specific(CmpLHS),
+ m_ZExt(m_Specific(CondVal)))))
+ return nullptr;
+
+ return CondVal;
+}
+
/// Try to simplify a select instruction when its condition operand is an
/// integer comparison.
static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
@@ -4761,6 +4800,9 @@ static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
}
}
+ if (Value *V = simplifySelectWithStrictICmp(CondVal, TrueVal, FalseVal, Q))
+ return V;
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstSimplify/select-icmp-relational.ll b/llvm/test/Transforms/InstSimplify/select-icmp-relational.ll
new file mode 100644
index 00000000000000..c2f120469c6b59
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/select-icmp-relational.ll
@@ -0,0 +1,423 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @ult_ule(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_ule_no_flags(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_no_flags(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @slt_sle(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @slt_sle(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp slt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp slt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp sle i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @slt_sle_no_flags(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @slt_sle_no_flags(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp slt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp slt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp sle i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_uge(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @sgt_sge(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @sgt_sge(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp sgt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp sgt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp sge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define <2 x i1> @ult_ule_splat_vector(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: define <2 x i1> @ult_ule_splat_vector(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult <2 x i8> [[X]], [[Y]]
+; CHECK-NEXT: ret <2 x i1> [[C1]]
+;
+ %c1 = icmp ult <2 x i8> %x, %y
+ %z = zext <2 x i1> %c1 to <2 x i8>
+ %add = add nuw <2 x i8> %x, %z
+ %c2 = icmp ule <2 x i8> %add, %y
+ %and = select <2 x i1> %c1, <2 x i1> %c2, <2 x i1> <i1 false, i1 false>
+ ret <2 x i1> %and
+}
+
+define <2 x i1> @ult_ule_vector_with_poison(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: define <2 x i1> @ult_ule_vector_with_poison(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult <2 x i8> [[X]], [[Y]]
+; CHECK-NEXT: ret <2 x i1> [[C1]]
+;
+ %c1 = icmp ult <2 x i8> %x, %y
+ %z = zext <2 x i1> %c1 to <2 x i8>
+ %add = add nuw <2 x i8> %x, %z
+ %c2 = icmp ule <2 x i8> %add, %y
+ %and = select <2 x i1> %c1, <2 x i1> %c2, <2 x i1> <i1 false, i1 poison>
+ ret <2 x i1> %and
+}
+
+define i1 @ugt_uge_with_poison(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge_with_poison(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, poison
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+declare void @use(i8)
+declare void @use_bit(i1)
+
+define i1 @ult_ule_multi_use(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_multi_use(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: call void @use_bit(i1 [[C1]])
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i8 [[ADD]], [[Y]]
+; CHECK-NEXT: call void @use_bit(i1 [[C2]])
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ call void @use_bit(i1 %c1)
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ call void @use_bit(i1 %c2)
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_ule_multi_use_add(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_multi_use_add(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: call void @use(i8 [[ADD]])
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ call void @use(i8 %add)
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_ule_commuted_binop(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_commuted_binop(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %z, %x
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_sext_sub_ule(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_sext_sub_ule(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = sext i1 %c1 to i8
+ %add = sub nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_uge_const_fold_false(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge_const_fold_false(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %c3 = icmp ult i8 10, 1
+ %and = select i1 %c1, i1 %c2, i1 %c3
+ ret i1 %and
+}
+
+define void @rust_inlclusive_noop(i8 noundef %n) unnamed_addr {
+; CHECK-LABEL: define void @rust_inlclusive_noop(
+; CHECK-SAME: i8 noundef [[N:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*]]:
+; CHECK-NEXT: br label %[[BB2_I:.*]]
+; CHECK: [[BB2_I]]:
+; CHECK-NEXT: [[ITER_SROA_0_07:%.*]] = phi i8 [ 0, %[[START]] ], [ [[SPEC_SELECT5:%.*]], %[[BB2_I]] ]
+; CHECK-NEXT: [[_0_I3_I:%.*]] = icmp ult i8 [[ITER_SROA_0_07]], [[N]]
+; CHECK-NEXT: [[_0_I4_I:%.*]] = zext i1 [[_0_I3_I]] to i8
+; CHECK-NEXT: [[SPEC_SELECT5]] = add nuw i8 [[ITER_SROA_0_07]], [[_0_I4_I]]
+; CHECK-NEXT: br i1 [[_0_I3_I]], label %[[BB2_I]], label %[[THEEND:.*]]
+; CHECK: [[THEEND]]:
+; CHECK-NEXT: ret void
+;
+start:
+ br label %bb2.i
+
+bb2.i:
+ %iter.sroa.0.07 = phi i8 [ 0, %start ], [ %spec.select5, %bb2.i ]
+ %_0.i3.i = icmp ult i8 %iter.sroa.0.07, %n
+ %_0.i4.i = zext i1 %_0.i3.i to i8
+ %spec.select5 = add nuw i8 %iter.sroa.0.07, %_0.i4.i
+ %_0.i.not.i = icmp ule i8 %spec.select5, %n
+ %or.cond.not = select i1 %_0.i3.i, i1 %_0.i.not.i, i1 false
+ br i1 %or.cond.not, label %bb2.i, label %theend
+
+theend:
+ ret void
+}
+
+define i1 @ule_ule_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ule_ule_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ule i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ule i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_ugt_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_ugt_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ugt i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ugt i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_sext_sub_uge_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_sext_sub_uge_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z_NEG:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], [[Z_NEG]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = sext i1 %c1 to i8
+ %add = sub nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @false_value_is_true_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @false_value_is_true_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[NOT_C1:%.*]] = xor i1 [[C1]], true
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[NOT_C1]], i1 true, i1 [[C2]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 true
+ ret i1 %and
+}
+
+define i1 @non_specific_operands_negative(i8 %x, i8 %y, i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @non_specific_operands_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[A]], [[B]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ult i8 %a, %b
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @sgt_nuw_sge_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @sgt_nuw_sge_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp sgt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp sge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp sgt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp sge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_nsw_uge_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_nsw_uge_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @non_strict_predicate_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @non_strict_predicate_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp eq i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_uge_no_flags_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge_no_flags_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @sgt_sge_no_flags_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @sgt_sge_no_flags_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp sgt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp sge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp sgt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp sge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
|
@llvm/pr-subscribers-llvm-transforms Author: Veera (veera-sivarajan) ChangesThis PR simplifies: Proof: https://alive2.llvm.org/ce/z/JFcDzM This helps improving codegen for Rust's loop over inclusive range with variable upper bound in rust-lang/rust#102462. Full diff: https://github.com/llvm/llvm-project/pull/118579.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp
index 01b0a089aab718..8e4731651ea59f 100644
--- a/llvm/lib/Analysis/InstructionSimplify.cpp
+++ b/llvm/lib/Analysis/InstructionSimplify.cpp
@@ -4639,6 +4639,45 @@ static Value *simplifySelectWithEquivalence(Value *CmpLHS, Value *CmpRHS,
return nullptr;
}
+/// Simplifies:
+/// `X > Y ? (X + zext(X > Y)) >= Y : false` to `X > Y`
+/// `X < Y ? (X + zext(X < Y)) <= Y : false` to `X < Y`
+static Value *simplifySelectWithStrictICmp(Value *CondVal, Value *TVal,
+ Value *FVal,
+ const SimplifyQuery &Q) {
+ if (!match(FVal, m_Zero()))
+ return nullptr;
+
+ ICmpInst::Predicate Pred;
+ Value *CmpLHS, *CmpRHS;
+
+ if (!match(CondVal, m_ICmp(Pred, m_Value(CmpLHS), m_Value(CmpRHS))))
+ return nullptr;
+
+ if (!CmpInst::isStrictPredicate(Pred))
+ return nullptr;
+
+ ICmpInst::Predicate NonStrictPred = ICmpInst::getNonStrictPredicate(Pred);
+ BinaryOperator *BinOp;
+
+ if (!match(TVal,
+ m_SpecificICmp(NonStrictPred, m_BinOp(BinOp), m_Specific(CmpRHS))))
+ return nullptr;
+
+ // This fold works for GT only when it does not wrap.
+ if (Pred == ICmpInst::ICMP_UGT && !Q.IIQ.hasNoUnsignedWrap(BinOp))
+ return nullptr;
+
+ if (Pred == ICmpInst::ICMP_SGT && !Q.IIQ.hasNoSignedWrap(BinOp))
+ return nullptr;
+
+ if (!match(BinOp, m_c_BinOp(Instruction::Add, m_Specific(CmpLHS),
+ m_ZExt(m_Specific(CondVal)))))
+ return nullptr;
+
+ return CondVal;
+}
+
/// Try to simplify a select instruction when its condition operand is an
/// integer comparison.
static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
@@ -4761,6 +4800,9 @@ static Value *simplifySelectWithICmpCond(Value *CondVal, Value *TrueVal,
}
}
+ if (Value *V = simplifySelectWithStrictICmp(CondVal, TrueVal, FalseVal, Q))
+ return V;
+
return nullptr;
}
diff --git a/llvm/test/Transforms/InstSimplify/select-icmp-relational.ll b/llvm/test/Transforms/InstSimplify/select-icmp-relational.ll
new file mode 100644
index 00000000000000..c2f120469c6b59
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/select-icmp-relational.ll
@@ -0,0 +1,423 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @ult_ule(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_ule_no_flags(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_no_flags(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @slt_sle(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @slt_sle(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp slt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp slt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp sle i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @slt_sle_no_flags(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @slt_sle_no_flags(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp slt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp slt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp sle i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_uge(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @sgt_sge(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @sgt_sge(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp sgt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp sgt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp sge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define <2 x i1> @ult_ule_splat_vector(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: define <2 x i1> @ult_ule_splat_vector(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult <2 x i8> [[X]], [[Y]]
+; CHECK-NEXT: ret <2 x i1> [[C1]]
+;
+ %c1 = icmp ult <2 x i8> %x, %y
+ %z = zext <2 x i1> %c1 to <2 x i8>
+ %add = add nuw <2 x i8> %x, %z
+ %c2 = icmp ule <2 x i8> %add, %y
+ %and = select <2 x i1> %c1, <2 x i1> %c2, <2 x i1> <i1 false, i1 false>
+ ret <2 x i1> %and
+}
+
+define <2 x i1> @ult_ule_vector_with_poison(<2 x i8> %x, <2 x i8> %y) {
+; CHECK-LABEL: define <2 x i1> @ult_ule_vector_with_poison(
+; CHECK-SAME: <2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult <2 x i8> [[X]], [[Y]]
+; CHECK-NEXT: ret <2 x i1> [[C1]]
+;
+ %c1 = icmp ult <2 x i8> %x, %y
+ %z = zext <2 x i1> %c1 to <2 x i8>
+ %add = add nuw <2 x i8> %x, %z
+ %c2 = icmp ule <2 x i8> %add, %y
+ %and = select <2 x i1> %c1, <2 x i1> %c2, <2 x i1> <i1 false, i1 poison>
+ ret <2 x i1> %and
+}
+
+define i1 @ugt_uge_with_poison(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge_with_poison(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: ret i1 false
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, poison
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+declare void @use(i8)
+declare void @use_bit(i1)
+
+define i1 @ult_ule_multi_use(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_multi_use(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: call void @use_bit(i1 [[C1]])
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i8 [[ADD]], [[Y]]
+; CHECK-NEXT: call void @use_bit(i1 [[C2]])
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ call void @use_bit(i1 %c1)
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ call void @use_bit(i1 %c2)
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_ule_multi_use_add(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_multi_use_add(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: call void @use(i8 [[ADD]])
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ call void @use(i8 %add)
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_ule_commuted_binop(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_ule_commuted_binop(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %z, %x
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ult_sext_sub_ule(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ult_sext_sub_ule(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ult i8 %x, %y
+ %z = sext i1 %c1 to i8
+ %add = sub nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_uge_const_fold_false(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge_const_fold_false(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[C1]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %c3 = icmp ult i8 10, 1
+ %and = select i1 %c1, i1 %c2, i1 %c3
+ ret i1 %and
+}
+
+define void @rust_inlclusive_noop(i8 noundef %n) unnamed_addr {
+; CHECK-LABEL: define void @rust_inlclusive_noop(
+; CHECK-SAME: i8 noundef [[N:%.*]]) unnamed_addr {
+; CHECK-NEXT: [[START:.*]]:
+; CHECK-NEXT: br label %[[BB2_I:.*]]
+; CHECK: [[BB2_I]]:
+; CHECK-NEXT: [[ITER_SROA_0_07:%.*]] = phi i8 [ 0, %[[START]] ], [ [[SPEC_SELECT5:%.*]], %[[BB2_I]] ]
+; CHECK-NEXT: [[_0_I3_I:%.*]] = icmp ult i8 [[ITER_SROA_0_07]], [[N]]
+; CHECK-NEXT: [[_0_I4_I:%.*]] = zext i1 [[_0_I3_I]] to i8
+; CHECK-NEXT: [[SPEC_SELECT5]] = add nuw i8 [[ITER_SROA_0_07]], [[_0_I4_I]]
+; CHECK-NEXT: br i1 [[_0_I3_I]], label %[[BB2_I]], label %[[THEEND:.*]]
+; CHECK: [[THEEND]]:
+; CHECK-NEXT: ret void
+;
+start:
+ br label %bb2.i
+
+bb2.i:
+ %iter.sroa.0.07 = phi i8 [ 0, %start ], [ %spec.select5, %bb2.i ]
+ %_0.i3.i = icmp ult i8 %iter.sroa.0.07, %n
+ %_0.i4.i = zext i1 %_0.i3.i to i8
+ %spec.select5 = add nuw i8 %iter.sroa.0.07, %_0.i4.i
+ %_0.i.not.i = icmp ule i8 %spec.select5, %n
+ %or.cond.not = select i1 %_0.i3.i, i1 %_0.i.not.i, i1 false
+ br i1 %or.cond.not, label %bb2.i, label %theend
+
+theend:
+ ret void
+}
+
+define i1 @ule_ule_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ule_ule_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ule i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ule i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_ugt_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_ugt_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ugt i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ugt i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_sext_sub_uge_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_sext_sub_uge_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z_NEG:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], [[Z_NEG]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = sext i1 %c1 to i8
+ %add = sub nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @false_value_is_true_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @false_value_is_true_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[NOT_C1:%.*]] = xor i1 [[C1]], true
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[NOT_C1]], i1 true, i1 [[C2]]
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 true
+ ret i1 %and
+}
+
+define i1 @non_specific_operands_negative(i8 %x, i8 %y, i8 %a, i8 %b) {
+; CHECK-LABEL: define i1 @non_specific_operands_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[A:%.*]], i8 [[B:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ult i8 [[A]], [[B]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp ule i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ult i8 %a, %b
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp ule i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @sgt_nuw_sge_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @sgt_nuw_sge_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp sgt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp sge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp sgt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nuw i8 %x, %z
+ %c2 = icmp sge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_nsw_uge_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_nsw_uge_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @non_strict_predicate_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @non_strict_predicate_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp eq i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp eq i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add nsw i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @ugt_uge_no_flags_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @ugt_uge_no_flags_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp uge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp ugt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp uge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
+
+define i1 @sgt_sge_no_flags_negative(i8 %x, i8 %y) {
+; CHECK-LABEL: define i1 @sgt_sge_no_flags_negative(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[C1:%.*]] = icmp sgt i8 [[X]], [[Y]]
+; CHECK-NEXT: [[Z:%.*]] = zext i1 [[C1]] to i8
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], [[Z]]
+; CHECK-NEXT: [[C2:%.*]] = icmp sge i8 [[ADD]], [[Y]]
+; CHECK-NEXT: [[AND:%.*]] = select i1 [[C1]], i1 [[C2]], i1 false
+; CHECK-NEXT: ret i1 [[AND]]
+;
+ %c1 = icmp sgt i8 %x, %y
+ %z = zext i1 %c1 to i8
+ %add = add i8 %x, %z
+ %c2 = icmp sge i8 %add, %y
+ %and = select i1 %c1, i1 %c2, i1 false
+ ret i1 %and
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two main issues:
- Logical and/or expressions are always canonicalized into bitwise versions. These pattern should be simplified by
simplifyAndInst/simplifyOrInst
: https://alive2.llvm.org/ce/z/kedJdF - We already implemented similar folds in
simplifyAndInst/simplifyOrInst
:llvm-project/llvm/lib/Analysis/InstructionSimplify.cpp
Lines 2212 to 2229 in 7be3326
if (Op0->getType()->isIntOrIntVectorTy(1)) { if (std::optional<bool> Implied = isImpliedCondition(Op0, Op1, Q.DL)) { // If Op0 is true implies Op1 is true, then Op0 is a subset of Op1. if (*Implied == true) return Op0; // If Op0 is true implies Op1 is false, then they are not true together. if (*Implied == false) return ConstantInt::getFalse(Op0->getType()); } if (std::optional<bool> Implied = isImpliedCondition(Op1, Op0, Q.DL)) { // If Op1 is true implies Op0 is true, then Op1 is a subset of Op0. if (*Implied) return Op1; // If Op1 is true implies Op0 is false, then they are not true together. if (!*Implied) return ConstantInt::getFalse(Op1->getType()); } } isImpliedCondICmps
.
Some suggestions:
- Add more tests.
X >= Y ? true : (X + zext(X >= Y)) > Y
: https://alive2.llvm.org/ce/z/QWa954(X < Y) & (X + zext(X < Y) <= Y)
(X >= Y) | (X + zext(X >= Y) > Y)
- Make sure that
isImpliedPoison((X + zext(X < Y)) <= Y, X < Y)
returns true - Add support for this pattern in
isImpliedCondICmps
.
As an alternative, we can replace the inner X < Y
with true
in InstCombine (via replaceInInstruction
). Then InstCombine will fold this expression into X < Y
:
X < Y ? (X +nuw zext(X < Y)) <= Y : false -->
X < Y ? (X +nuw 1) <= Y : false -->
X < Y ? X < Y : false -->
X < Y
I don't think this would cover the motivating case, where the add is multi-use. |
The motivating case for this optimization is:
In this case, the call to This is because So I don't know how to make Footnotes |
Sure. |
This PR simplifies:
X > Y ? (X + zext(X > Y)) >= Y : false
toX > Y
X < Y ? (X + zext(X < Y)) <= Y : false
toX < Y
Proof: https://alive2.llvm.org/ce/z/JFcDzM
This helps improving codegen for Rust's loop over inclusive range with variable upper bound in rust-lang/rust#102462.