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(&I))); break; @@ -1448,6 +1450,28 @@ static bool checkAndReplaceMinMax(MinMaxIntrinsic *MinMax, ConstraintInfo &Info, return false; } +static bool checkAndReplaceCmp(CmpIntrinsic *I, ConstraintInfo &Info, + SmallVectorImpl &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(Inst)) { Changed |= checkAndReplaceMinMax(MinMax, Info, ToRemove); + } else if (auto *CmpIntr = dyn_cast(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 +}