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

via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 10 13:00:01 PDT 2024


Author: Poseydon42
Date: 2024-07-10T21:59:59+02:00
New Revision: d18ca43edc6fed920db86a99ed9f7e3bcafc99d2

URL: https://github.com/llvm/llvm-project/commit/d18ca43edc6fed920db86a99ed9f7e3bcafc99d2
DIFF: https://github.com/llvm/llvm-project/commit/d18ca43edc6fed920db86a99ed9f7e3bcafc99d2.diff

LOG: [ConstraintElimination] Add support for UCMP/SCMP intrinsics (#97974)

This adds checks to fold calls to `ucmp`/`scmp` where a comparative
relationship between the arguments can be established.

Added: 
    llvm/test/Transforms/ConstraintElimination/uscmp.ll

Modified: 
    llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 52d9969426105..c31173879af1e 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1087,6 +1087,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;
@@ -1448,6 +1450,28 @@ static bool checkAndReplaceMinMax(MinMaxIntrinsic *MinMax, ConstraintInfo &Info,
   return false;
 }
 
+static bool checkAndReplaceCmp(CmpIntrinsic *I, ConstraintInfo &Info,
+                               SmallVectorImpl<Instruction *> &ToRemove) {
+  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(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, I, Info)) {
+    I->replaceAllUsesWith(ConstantInt::get(I->getType(), 0));
+    ToRemove.push_back(I);
+    return true;
+  }
+  return false;
+}
+
 static void
 removeEntryFromStack(const StackEntry &E, ConstraintInfo &Info,
                      Module *ReproducerModule,
@@ -1750,6 +1774,8 @@ 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 *CmpIntr = dyn_cast<CmpIntrinsic>(Inst)) {
+        Changed |= checkAndReplaceCmp(CmpIntr, 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..63ac050f2c3c5
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/uscmp.ll
@@ -0,0 +1,149 @@
+; 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
+}
+
+; Negative test: signedness mismatch
+define i8 @scmp_4(i32 %x, i32 %y) {
+; CHECK-LABEL: @scmp_4(
+; CHECK-NEXT:    [[COND:%.*]] = icmp ugt i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 20
+; CHECK:       false:
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.scmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %cond = icmp ugt 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_4(i32 %x, i32 %y) {
+; CHECK-LABEL: @ucmp_4(
+; CHECK-NEXT:    [[COND:%.*]] = icmp slt i32 [[X:%.*]], [[Y:%.*]]
+; CHECK-NEXT:    br i1 [[COND]], label [[TRUE:%.*]], label [[FALSE:%.*]]
+; CHECK:       true:
+; CHECK-NEXT:    ret i8 20
+; CHECK:       false:
+; CHECK-NEXT:    [[R:%.*]] = call i8 @llvm.ucmp.i8.i32(i32 [[X]], i32 [[Y]])
+; CHECK-NEXT:    ret i8 [[R]]
+;
+  %cond = icmp slt 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
+}


        


More information about the llvm-commits mailing list