-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[InstCombine] Implement folds of icmp of UCMP/SCMP call and a constant #96118
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
Conversation
@llvm/pr-subscribers-llvm-transforms Author: None (Poseydon42) ChangesThis patch handles various cases where an operation of the kind I wasn't sure what negative tests should be added here, if any are necessary at all. I'd love to hear your suggestions. Proofs (ucmp): https://alive2.llvm.org/ce/z/qQ7ihz Full diff: https://github.com/llvm/llvm-project/pull/96118.diff 3 Files Affected:
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
index 34b0f8b860497..cb0351259a1f9 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp
@@ -3926,6 +3926,92 @@ foldICmpUSubSatOrUAddSatWithConstant(ICmpInst::Predicate Pred,
ConstantInt::get(Op1->getType(), EquivInt));
}
+static Instruction *
+foldICmpOfCmpIntrinsicWithConstant(ICmpInst::Predicate Pred, IntrinsicInst *I,
+ const APInt &C,
+ InstCombiner::BuilderTy &Builder) {
+ Intrinsic::ID IID = I->getIntrinsicID();
+ Value *LHS = I->getOperand(0);
+ Value *RHS = I->getOperand(1);
+
+ switch (Pred) {
+ case ICmpInst::ICMP_EQ:
+ if (C.isZero())
+ return new ICmpInst(Pred, LHS, RHS);
+ if (C.isOne())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SGT
+ : ICmpInst::ICMP_UGT,
+ LHS, RHS);
+ if (C.isAllOnes())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SLT
+ : ICmpInst::ICMP_ULT,
+ LHS, RHS);
+ break;
+
+ case ICmpInst::ICMP_NE:
+ if (C.isZero())
+ return new ICmpInst(Pred, LHS, RHS);
+ if (C.isOne())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SLE
+ : ICmpInst::ICMP_ULE,
+ LHS, RHS);
+ if (C.isAllOnes())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SGE
+ : ICmpInst::ICMP_UGE,
+ LHS, RHS);
+ break;
+
+ case ICmpInst::ICMP_SGT:
+ if (C.isAllOnes())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SGE
+ : ICmpInst::ICMP_UGE,
+ LHS, RHS);
+ if (C.isZero())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SGT
+ : ICmpInst::ICMP_UGT,
+ LHS, RHS);
+ break;
+
+ case ICmpInst::ICMP_SGE:
+ if (C.isZero())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SGE
+ : ICmpInst::ICMP_UGE,
+ LHS, RHS);
+ if (C.isOne())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SGT
+ : ICmpInst::ICMP_UGT,
+ LHS, RHS);
+ break;
+
+ case ICmpInst::ICMP_SLT:
+ if (C.isZero())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SLT
+ : ICmpInst::ICMP_ULT,
+ LHS, RHS);
+ if (C.isOne())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SLE
+ : ICmpInst::ICMP_ULE,
+ LHS, RHS);
+ break;
+
+ case llvm::ICmpInst::ICMP_SLE:
+ if (C.isZero())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SLE
+ : ICmpInst::ICMP_ULE,
+ LHS, RHS);
+ if (C.isAllOnes())
+ return new ICmpInst(IID == Intrinsic::scmp ? ICmpInst::ICMP_SLT
+ : ICmpInst::ICMP_ULT,
+ LHS, RHS);
+ break;
+
+ default:
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
/// Fold an icmp with LLVM intrinsic and constant operand: icmp Pred II, C.
Instruction *InstCombinerImpl::foldICmpIntrinsicWithConstant(ICmpInst &Cmp,
IntrinsicInst *II,
@@ -3947,6 +4033,11 @@ Instruction *InstCombinerImpl::foldICmpIntrinsicWithConstant(ICmpInst &Cmp,
if (Instruction *R = foldCtpopPow2Test(Cmp, II, C, Builder, Q))
return R;
} break;
+ case Intrinsic::scmp:
+ case Intrinsic::ucmp:
+ if (auto *Folded = foldICmpOfCmpIntrinsicWithConstant(Pred, II, C, Builder))
+ return Folded;
+ break;
}
if (Cmp.isEquality())
diff --git a/llvm/test/Transforms/InstCombine/scmp.ll b/llvm/test/Transforms/InstCombine/scmp.ll
new file mode 100644
index 0000000000000..4f903a79afd5d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/scmp.ll
@@ -0,0 +1,156 @@
+; 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 @scmp_eq_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_eq_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp eq i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @scmp_ne_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_ne_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp ne i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @scmp_eq_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_eq_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp eq i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @scmp_ne_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_ne_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sle i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp ne i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @scmp_eq_negative_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_eq_negative_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp eq i8 %1, -1
+ ret i1 %2
+}
+
+define i1 @scmp_ne_negative_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_ne_negative_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp ne i8 %1, -1
+ ret i1 %2
+}
+
+define i1 @scmp_sgt_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_sgt_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp sgt i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @scmp_sgt_neg_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_sgt_neg_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp sgt i8 %1, -1
+ ret i1 %2
+}
+
+define i1 @scmp_sge_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_sge_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sge i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp sge i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @scmp_sge_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_sge_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp sge i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @scmp_slt_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_slt_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp slt i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @scmp_slt_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_slt_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sle i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp slt i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @scmp_sle_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_sle_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sle i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp sle i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @scmp_sle_neg_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @scmp_sle_neg_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.scmp(i32 %x, i32 %y)
+ %2 = icmp sle i8 %1, -1
+ ret i1 %2
+}
diff --git a/llvm/test/Transforms/InstCombine/ucmp.ll b/llvm/test/Transforms/InstCombine/ucmp.ll
new file mode 100644
index 0000000000000..9ab67560c9117
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/ucmp.ll
@@ -0,0 +1,156 @@
+; 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 @ucmp_eq_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_eq_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp eq i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp eq i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @ucmp_ne_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_ne_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ne i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp ne i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @ucmp_eq_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_eq_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp eq i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @ucmp_ne_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_ne_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp ne i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @ucmp_eq_negative_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_eq_negative_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp eq i8 %1, -1
+ ret i1 %2
+}
+
+define i1 @ucmp_ne_negative_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_ne_negative_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp uge i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp ne i8 %1, -1
+ ret i1 %2
+}
+
+define i1 @ucmp_sgt_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_sgt_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp sgt i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @ucmp_sgt_neg_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_sgt_neg_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp uge i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp sgt i8 %1, -1
+ ret i1 %2
+}
+
+define i1 @ucmp_sge_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_sge_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp uge i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp sge i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @ucmp_sge_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_sge_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ugt i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp sge i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @ucmp_slt_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_slt_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp slt i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @ucmp_slt_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_slt_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp slt i8 %1, 1
+ ret i1 %2
+}
+
+define i1 @ucmp_sle_0(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_sle_0(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ule i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp sle i8 %1, 0
+ ret i1 %2
+}
+
+define i1 @ucmp_sle_neg_1(i32 %x, i32 %y) {
+; CHECK-LABEL: define i1 @ucmp_sle_neg_1(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[TMP2:%.*]] = icmp ult i32 [[X]], [[Y]]
+; CHECK-NEXT: ret i1 [[TMP2]]
+;
+ %1 = call i8 @llvm.ucmp(i32 %x, i32 %y)
+ %2 = icmp sle i8 %1, -1
+ ret i1 %2
+}
|
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.
LGTM. Thank you!
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.
LGTM
Co-authored-by: Nikita Popov <[email protected]>
llvm#96118) This patch handles various cases where an operation of the kind `icmp (ucmp/scmp x, y), constant` folds to `icmp x, y`. Another patch with cases where this operation folds to a constant (i.e. dumb cases like `icmp eq (cmp x, y), 4` should be published in a couple of days. I wasn't sure what negative tests should be added here, if any are necessary at all. I'd love to hear your suggestions. Proofs (ucmp): https://alive2.llvm.org/ce/z/qQ7ihz Proofs (scmp): https://alive2.llvm.org/ce/z/cipKEn --------- Co-authored-by: Nikita Popov <[email protected]>
This patch handles various cases where an operation of the kind
icmp (ucmp/scmp x, y), constant
folds toicmp x, y
. Another patch with cases where this operation folds to a constant (i.e. dumb cases likeicmp eq (cmp x, y), 4
should be published in a couple of days.I wasn't sure what negative tests should be added here, if any are necessary at all. I'd love to hear your suggestions.
Proofs (ucmp): https://alive2.llvm.org/ce/z/qQ7ihz
Proofs (scmp): https://alive2.llvm.org/ce/z/cipKEn