[llvm] [ConstraintElimination] Add support for UCMP/SCMP intrinsics (PR #97974)

via llvm-commits llvm-commits at lists.llvm.org
Tue Jul 9 04:33:11 PDT 2024


https://github.com/Poseydon42 updated https://github.com/llvm/llvm-project/pull/97974

>From c9d37da7f2992288e5bb95494ead7ae4280e4de4 Mon Sep 17 00:00:00 2001
From: Poseydon42 <vvmposeydon at gmail.com>
Date: Sun, 7 Jul 2024 23:00:38 +0100
Subject: [PATCH 1/2] [ConstraintElimination] Add support for UCMP/SCMP
 intrinsics

---
 .../Scalar/ConstraintElimination.cpp          |  34 ++++++
 .../Transforms/ConstraintElimination/uscmp.ll | 110 ++++++++++++++++++
 2 files changed, 144 insertions(+)
 create mode 100644 llvm/test/Transforms/ConstraintElimination/uscmp.ll

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index bf0c67d9dbc4f..478aea7287875 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1073,6 +1073,8 @@ void State::addInfoFor(BasicBlock &BB) {
     }
     // Enqueue ssub_with_overflow for simplification.
     case Intrinsic::ssub_with_overflow:
+    case Intrinsic::ucmp:
+    case Intrinsic::scmp:
       WorkList.push_back(
           FactOrCheck::getCheck(DT.getNode(&BB), cast<CallInst>(&I)));
       break;
@@ -1434,6 +1436,33 @@ static bool checkAndReplaceMinMax(MinMaxIntrinsic *MinMax, ConstraintInfo &Info,
   return false;
 }
 
+static bool checkAndReplaceCmp(IntrinsicInst *II, ConstraintInfo &Info,
+                               SmallVectorImpl<Instruction *> &ToRemove) {
+  bool IsSigned = II->getIntrinsicID() == Intrinsic::scmp;
+  Value *LHS = II->getOperand(0);
+  Value *RHS = II->getOperand(1);
+  if (checkCondition(IsSigned ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT, LHS,
+                     RHS, II, Info)
+          .value_or(false)) {
+    II->replaceAllUsesWith(ConstantInt::get(II->getType(), 1));
+    ToRemove.push_back(II);
+    return true;
+  }
+  if (checkCondition(IsSigned ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT, LHS,
+                     RHS, II, Info)
+          .value_or(false)) {
+    II->replaceAllUsesWith(ConstantInt::getSigned(II->getType(), -1));
+    ToRemove.push_back(II);
+    return true;
+  }
+  if (checkCondition(ICmpInst::ICMP_EQ, LHS, RHS, II, Info).value_or(false)) {
+    II->replaceAllUsesWith(ConstantInt::get(II->getType(), 0));
+    ToRemove.push_back(II);
+    return true;
+  }
+  return false;
+}
+
 static void
 removeEntryFromStack(const StackEntry &E, ConstraintInfo &Info,
                      Module *ReproducerModule,
@@ -1736,6 +1765,11 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
         Changed |= Simplified;
       } else if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(Inst)) {
         Changed |= checkAndReplaceMinMax(MinMax, Info, ToRemove);
+      } else if (auto *CmpIntrinsic = dyn_cast<IntrinsicInst>(Inst);
+                 CmpIntrinsic &&
+                 (CmpIntrinsic->getIntrinsicID() == Intrinsic::scmp ||
+                  CmpIntrinsic->getIntrinsicID() == Intrinsic::ucmp)) {
+        Changed |= checkAndReplaceCmp(CmpIntrinsic, Info, ToRemove);
       }
       continue;
     }
diff --git a/llvm/test/Transforms/ConstraintElimination/uscmp.ll b/llvm/test/Transforms/ConstraintElimination/uscmp.ll
new file mode 100644
index 0000000000000..16ca93f0427e7
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/uscmp.ll
@@ -0,0 +1,110 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+define i8 @scmp_1(i32 %x, i32 %y) {
+; CHECK-LABEL: @scmp_1(
+; CHECK-NEXT:    [[COND:%.*]] = icmp sgt i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 1
+; CHECK:       false:
+; CHECK-NEXT:    ret i8 20
+;
+  %cond = icmp sgt i32 %x, %y
+  br i1 %cond, label %true, label %false
+true:
+  %r = call i8 @llvm.scmp(i32 %x, i32 %y)
+  ret i8 %r
+false:
+  ret i8 20
+}
+
+define i8 @ucmp_1(i32 %x, i32 %y) {
+; CHECK-LABEL: @ucmp_1(
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 -1
+; CHECK:       false:
+; CHECK-NEXT:    ret i8 20
+;
+  %cond = icmp ult i32 %x, %y
+  br i1 %cond, label %true, label %false
+true:
+  %r = call i8 @llvm.ucmp(i32 %x, i32 %y)
+  ret i8 %r
+false:
+  ret i8 20
+}
+
+define i8 @scmp_2(i32 %x, i32 %y) {
+; CHECK-LABEL: @scmp_2(
+; CHECK-NEXT:    [[COND:%.*]] = icmp sge i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 20
+; CHECK:       false:
+; CHECK-NEXT:    ret i8 -1
+;
+  %cond = icmp sge i32 %x, %y
+  br i1 %cond, label %true, label %false
+true:
+  ret i8 20
+false:
+  %r = call i8 @llvm.scmp(i32 %x, i32 %y)
+  ret i8 %r
+}
+
+define i8 @ucmp_2(i32 %x, i32 %y) {
+; CHECK-LABEL: @ucmp_2(
+; CHECK-NEXT:    [[COND:%.*]] = icmp ule i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 20
+; CHECK:       false:
+; CHECK-NEXT:    ret i8 1
+;
+  %cond = icmp ule i32 %x, %y
+  br i1 %cond, label %true, label %false
+true:
+  ret i8 20
+false:
+  %r = call i8 @llvm.ucmp(i32 %x, i32 %y)
+  ret i8 %r
+}
+
+define i8 @scmp_3(i32 %x, i32 %y) {
+; CHECK-LABEL: @scmp_3(
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 0
+; CHECK:       false:
+; CHECK-NEXT:    ret i8 20
+;
+  %cond = icmp eq i32 %x, %y
+  br i1 %cond, label %true, label %false
+true:
+  %r = call i8 @llvm.scmp(i32 %x, i32 %y)
+  ret i8 %r
+false:
+  ret i8 20
+}
+
+define i8 @ucmp_3(i32 %x, i32 %y) {
+; CHECK-LABEL: @ucmp_3(
+; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 0
+; CHECK:       false:
+; CHECK-NEXT:    ret i8 20
+;
+  %cond = icmp eq i32 %x, %y
+  br i1 %cond, label %true, label %false
+true:
+  %r = call i8 @llvm.ucmp(i32 %x, i32 %y)
+  ret i8 %r
+false:
+  ret i8 20
+}

>From c70dcfcae283eef4dff657c60c9dfbbd76b46f87 Mon Sep 17 00:00:00 2001
From: Poseydon42 <vvmposeydon at gmail.com>
Date: Tue, 9 Jul 2024 12:23:45 +0100
Subject: [PATCH 2/2] Add CmpIntrinsic class to improve readability of the code

---
 llvm/include/llvm/IR/IntrinsicInst.h          | 37 +++++++++++++++++++
 .../Scalar/ConstraintElimination.cpp          | 34 +++++++----------
 2 files changed, 50 insertions(+), 21 deletions(-)

diff --git a/llvm/include/llvm/IR/IntrinsicInst.h b/llvm/include/llvm/IR/IntrinsicInst.h
index 3963a5c8ab8f9..2a37c06dd2c3c 100644
--- a/llvm/include/llvm/IR/IntrinsicInst.h
+++ b/llvm/include/llvm/IR/IntrinsicInst.h
@@ -834,6 +834,43 @@ class MinMaxIntrinsic : public IntrinsicInst {
   }
 };
 
+/// This class represents a ucmp/scmp intrinsic
+class CmpIntrinsic : public IntrinsicInst {
+public:
+  static bool classof(const IntrinsicInst *I) {
+    switch (I->getIntrinsicID()) {
+    case Intrinsic::scmp:
+    case Intrinsic::ucmp:
+      return true;
+    default:
+      return false;
+    }
+  }
+  static bool classof(const Value *V) {
+    return isa<IntrinsicInst>(V) && classof(cast<IntrinsicInst>(V));
+  }
+
+  Value *getLHS() const { return const_cast<Value *>(getArgOperand(0)); }
+  Value *getRHS() const { return const_cast<Value *>(getArgOperand(1)); }
+
+  static bool isSigned(Intrinsic::ID ID) { return ID == Intrinsic::scmp; }
+  bool isSigned() const { return isSigned(getIntrinsicID()); }
+
+  static CmpInst::Predicate getGTPredicate(Intrinsic::ID ID) {
+    return isSigned(ID) ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT;
+  }
+  CmpInst::Predicate getGTPredicate() const {
+    return getGTPredicate(getIntrinsicID());
+  }
+
+  static CmpInst::Predicate getLTPredicate(Intrinsic::ID ID) {
+    return isSigned(ID) ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT;
+  }
+  CmpInst::Predicate getLTPredicate() const {
+    return getLTPredicate(getIntrinsicID());
+  }
+};
+
 /// This class represents an intrinsic that is based on a binary operation.
 /// This includes op.with.overflow and saturating add/sub intrinsics.
 class BinaryOpIntrinsic : public IntrinsicInst {
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 478aea7287875..9c9d566ffc584 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1436,28 +1436,23 @@ static bool checkAndReplaceMinMax(MinMaxIntrinsic *MinMax, ConstraintInfo &Info,
   return false;
 }
 
-static bool checkAndReplaceCmp(IntrinsicInst *II, ConstraintInfo &Info,
+static bool checkAndReplaceCmp(CmpIntrinsic *I, ConstraintInfo &Info,
                                SmallVectorImpl<Instruction *> &ToRemove) {
-  bool IsSigned = II->getIntrinsicID() == Intrinsic::scmp;
-  Value *LHS = II->getOperand(0);
-  Value *RHS = II->getOperand(1);
-  if (checkCondition(IsSigned ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT, LHS,
-                     RHS, II, Info)
-          .value_or(false)) {
-    II->replaceAllUsesWith(ConstantInt::get(II->getType(), 1));
-    ToRemove.push_back(II);
+  Value *LHS = I->getOperand(0);
+  Value *RHS = I->getOperand(1);
+  if (checkCondition(I->getGTPredicate(), LHS, RHS, I, Info).value_or(false)) {
+    I->replaceAllUsesWith(ConstantInt::get(I->getType(), 1));
+    ToRemove.push_back(I);
     return true;
   }
-  if (checkCondition(IsSigned ? ICmpInst::ICMP_SLT : ICmpInst::ICMP_ULT, LHS,
-                     RHS, II, Info)
-          .value_or(false)) {
-    II->replaceAllUsesWith(ConstantInt::getSigned(II->getType(), -1));
-    ToRemove.push_back(II);
+  if (checkCondition(I->getLTPredicate(), LHS, RHS, I, Info).value_or(false)) {
+    I->replaceAllUsesWith(ConstantInt::getSigned(I->getType(), -1));
+    ToRemove.push_back(I);
     return true;
   }
-  if (checkCondition(ICmpInst::ICMP_EQ, LHS, RHS, II, Info).value_or(false)) {
-    II->replaceAllUsesWith(ConstantInt::get(II->getType(), 0));
-    ToRemove.push_back(II);
+  if (checkCondition(ICmpInst::ICMP_EQ, LHS, RHS, I, Info)) {
+    I->replaceAllUsesWith(ConstantInt::get(I->getType(), 0));
+    ToRemove.push_back(I);
     return true;
   }
   return false;
@@ -1765,10 +1760,7 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
         Changed |= Simplified;
       } else if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(Inst)) {
         Changed |= checkAndReplaceMinMax(MinMax, Info, ToRemove);
-      } else if (auto *CmpIntrinsic = dyn_cast<IntrinsicInst>(Inst);
-                 CmpIntrinsic &&
-                 (CmpIntrinsic->getIntrinsicID() == Intrinsic::scmp ||
-                  CmpIntrinsic->getIntrinsicID() == Intrinsic::ucmp)) {
+      } else if (auto *CmpIntrinsic = dyn_cast<class CmpIntrinsic>(Inst)) {
         Changed |= checkAndReplaceCmp(CmpIntrinsic, Info, ToRemove);
       }
       continue;



More information about the llvm-commits mailing list