From d196008bc5d133cb81642151c174c2a38baebeeb Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Fri, 18 Apr 2025 17:00:00 +0800 Subject: [PATCH 1/9] [InstCombine] Add pre-commit tests. NFC. --- .../Transforms/InstCombine/icmp-select.ll | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index 0bdbc88ba67c6..aafb85395f975 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -628,3 +628,141 @@ define i1 @icmp_slt_select(i1 %cond, i32 %a, i32 %b) { %res = icmp slt i32 %lhs, %rhs ret i1 %res } + +define i1 @discr_eq(i8 %a, i8 %b) { +; CHECK-LABEL: @discr_eq( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 +; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, -2 + %cmp1 = icmp ugt i8 %a, 1 + %sel1 = select i1 %cmp1, i8 %add1, i8 1 + %add2 = add i8 %b, -2 + %cmp2 = icmp ugt i8 %b, 1 + %sel2 = select i1 %cmp2, i8 %add2, i8 1 + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} + +define i1 @discr_ne(i8 %a, i8 %b) { +; CHECK-LABEL: @discr_ne( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 +; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1 +; CHECK-NEXT: [[RES:%.*]] = icmp ne i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, -2 + %cmp1 = icmp ugt i8 %a, 1 + %sel1 = select i1 %cmp1, i8 %add1, i8 1 + %add2 = add i8 %b, -2 + %cmp2 = icmp ugt i8 %b, 1 + %sel2 = select i1 %cmp2, i8 %add2, i8 1 + %res = icmp ne i8 %sel1, %sel2 + ret i1 %res +} + +define i1 @discr_xor_eq(i8 %a, i8 %b) { +; CHECK-LABEL: @discr_xor_eq( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[XOR1:%.*]] = xor i8 [[A:%.*]], -3 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[XOR1]], i8 1 +; CHECK-NEXT: [[XOR2:%.*]] = xor i8 [[B:%.*]], -3 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[XOR2]], i8 1 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %xor1 = xor i8 %a, -3 + %cmp1 = icmp ugt i8 %a, 1 + %sel1 = select i1 %cmp1, i8 %xor1, i8 1 + %xor2 = xor i8 %b, -3 + %cmp2 = icmp ugt i8 %b, 1 + %sel2 = select i1 %cmp2, i8 %xor2, i8 1 + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} + +define i1 @discr_eq_simple(i8 %a, i8 %b) { +; CHECK-LABEL: @discr_eq_simple( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 +; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[ADD2]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, -2 + %cmp1 = icmp ugt i8 %a, 1 + %sel1 = select i1 %cmp1, i8 %add1, i8 1 + %add2 = add i8 %b, -2 + %res = icmp eq i8 %sel1, %add2 + ret i1 %res +} + +; Negative tests + +define i1 @discr_eq_multi_use(i8 %a, i8 %b) { +; CHECK-LABEL: @discr_eq_multi_use( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 +; CHECK-NEXT: call void @use(i8 [[SEL1]]) +; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, -2 + %cmp1 = icmp ugt i8 %a, 1 + %sel1 = select i1 %cmp1, i8 %add1, i8 1 + call void @use(i8 %sel1) + %add2 = add i8 %b, -2 + %cmp2 = icmp ugt i8 %b, 1 + %sel2 = select i1 %cmp2, i8 %add2, i8 1 + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} + +define i1 @discr_eq_failed_to_simplify(i8 %a, i8 %b) { +; CHECK-LABEL: @discr_eq_failed_to_simplify( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -3 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 +; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, -3 + %cmp1 = icmp ugt i8 %a, 1 + %sel1 = select i1 %cmp1, i8 %add1, i8 1 + %add2 = add i8 %b, -2 + %cmp2 = icmp ugt i8 %b, 1 + %sel2 = select i1 %cmp2, i8 %add2, i8 1 + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} From 4951cae2340cc8d83b4da34b1a608f9675da3cb4 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Fri, 18 Apr 2025 17:04:47 +0800 Subject: [PATCH 2/9] [InstCombine] Offset both sides of an equality icmp --- .../InstCombine/InstCombineCompares.cpp | 129 ++++++++++++++++++ llvm/test/Transforms/InstCombine/icmp-add.ll | 6 +- .../InstCombine/icmp-equality-xor.ll | 3 +- .../Transforms/InstCombine/icmp-select.ll | 38 ++---- 4 files changed, 147 insertions(+), 29 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index b7b0bb7361359..aa5da226001a4 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -5808,6 +5808,131 @@ static Instruction *foldICmpPow2Test(ICmpInst &I, return nullptr; } +/// Find all possible pairs (BinOp, RHS) that BinOp V, RHS can be simplified. +using OffsetOp = std::pair; +static void collectOffsetOp(Value *V, SmallVectorImpl &Offsets, + bool AllowRecursion) { + Instruction *Inst = dyn_cast(V); + if (!Inst) + return; + Constant *C; + + switch (Inst->getOpcode()) { + case Instruction::Add: + if (match(Inst->getOperand(1), m_ImmConstant(C))) + if (Constant *NegC = ConstantExpr::getNeg(C)) + Offsets.emplace_back(Instruction::Add, NegC); + break; + case Instruction::Xor: + Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); + Offsets.emplace_back(Instruction::Xor, Inst->getOperand(0)); + break; + case Instruction::Select: + if (AllowRecursion) { + Value *TrueV = Inst->getOperand(1); + if (TrueV->hasOneUse()) + collectOffsetOp(TrueV, Offsets, /*AllowRecursion=*/false); + Value *FalseV = Inst->getOperand(2); + if (FalseV->hasOneUse()) + collectOffsetOp(FalseV, Offsets, /*AllowRecursion=*/false); + } + break; + default: + break; + } +} + +enum class OffsetKind { Invalid, Value, Select }; + +struct OffsetResult { + OffsetKind Kind; + Value *V0, *V1, *V2; + + static OffsetResult invalid() { + return {OffsetKind::Invalid, nullptr, nullptr, nullptr}; + } + static OffsetResult value(Value *V) { + return {OffsetKind::Value, V, nullptr, nullptr}; + } + static OffsetResult select(Value *Cond, Value *TrueV, Value *FalseV) { + return {OffsetKind::Select, Cond, TrueV, FalseV}; + } + bool isValid() const { return Kind != OffsetKind::Invalid; } + Value *materialize(InstCombiner::BuilderTy &Builder) const { + switch (Kind) { + case OffsetKind::Invalid: + llvm_unreachable("Invalid offset result"); + case OffsetKind::Value: + return V0; + case OffsetKind::Select: + return Builder.CreateSelect(V0, V1, V2); + default: + llvm_unreachable("Unknown offset result kind"); + } + } +}; + +/// Offset both sides of an equality icmp to see if we can save some +/// instructions: icmp eq/ne X, Y -> icmp eq/ne X op Z, Y op Z. +/// Note: This operation should not introduce poison. +static Instruction *foldICmpEqualityWithOffset(ICmpInst &I, + InstCombiner::BuilderTy &Builder, + const SimplifyQuery &SQ) { + assert(I.isEquality() && "Expected an equality icmp"); + Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1); + if (!Op0->getType()->isIntOrIntVectorTy()) + return nullptr; + + SmallVector OffsetOps; + if (Op0->hasOneUse()) + collectOffsetOp(Op0, OffsetOps, /*AllowRecursion=*/true); + if (Op1->hasOneUse()) + collectOffsetOp(Op1, OffsetOps, /*AllowRecursion=*/true); + + auto ApplyOffsetImpl = [&](Value *V, unsigned BinOpc, Value *RHS) -> Value * { + Value *Simplified = simplifyBinOp(BinOpc, V, RHS, SQ); + // Avoid infinite loops by checking if RHS is an identity for the BinOp. + if (!Simplified || Simplified == V) + return nullptr; + return Simplified; + }; + + auto ApplyOffset = [&](Value *V, unsigned BinOpc, + Value *RHS) -> OffsetResult { + if (auto *Sel = dyn_cast(V)) { + if (!Sel->hasOneUse()) + return OffsetResult::invalid(); + Value *TrueVal = ApplyOffsetImpl(Sel->getTrueValue(), BinOpc, RHS); + if (!TrueVal) + return OffsetResult::invalid(); + Value *FalseVal = ApplyOffsetImpl(Sel->getFalseValue(), BinOpc, RHS); + if (!FalseVal) + return OffsetResult::invalid(); + return OffsetResult::select(Sel->getCondition(), TrueVal, FalseVal); + } + if (Value *Simplified = ApplyOffsetImpl(V, BinOpc, RHS)) + return OffsetResult::value(Simplified); + return OffsetResult::invalid(); + }; + + for (auto [BinOp, RHS] : OffsetOps) { + auto BinOpc = static_cast(BinOp); + + auto Op0Result = ApplyOffset(Op0, BinOpc, RHS); + if (!Op0Result.isValid()) + continue; + auto Op1Result = ApplyOffset(Op1, BinOpc, RHS); + if (!Op1Result.isValid()) + continue; + + Value *NewLHS = Op0Result.materialize(Builder); + Value *NewRHS = Op1Result.materialize(Builder); + return new ICmpInst(I.getPredicate(), NewLHS, NewRHS); + } + + return nullptr; +} + Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) { if (!I.isEquality()) return nullptr; @@ -6054,6 +6179,10 @@ Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) { : ConstantInt::getNullValue(A->getType())); } + if (auto *Res = foldICmpEqualityWithOffset( + I, Builder, getSimplifyQuery().getWithInstruction(&I))) + return Res; + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/icmp-add.ll b/llvm/test/Transforms/InstCombine/icmp-add.ll index a8cdf80948a84..1a41c1f3e1045 100644 --- a/llvm/test/Transforms/InstCombine/icmp-add.ll +++ b/llvm/test/Transforms/InstCombine/icmp-add.ll @@ -2380,8 +2380,7 @@ define <2 x i1> @icmp_eq_add_non_splat(<2 x i32> %a) { define <2 x i1> @icmp_eq_add_undef2(<2 x i32> %a) { ; CHECK-LABEL: @icmp_eq_add_undef2( -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[A:%.*]], splat (i32 5) -; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[ADD]], +; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], ; CHECK-NEXT: ret <2 x i1> [[CMP]] ; %add = add <2 x i32> %a, @@ -2391,8 +2390,7 @@ define <2 x i1> @icmp_eq_add_undef2(<2 x i32> %a) { define <2 x i1> @icmp_eq_add_non_splat2(<2 x i32> %a) { ; CHECK-LABEL: @icmp_eq_add_non_splat2( -; CHECK-NEXT: [[ADD:%.*]] = add <2 x i32> [[A:%.*]], splat (i32 5) -; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[ADD]], +; CHECK-NEXT: [[CMP:%.*]] = icmp eq <2 x i32> [[A:%.*]], ; CHECK-NEXT: ret <2 x i1> [[CMP]] ; %add = add <2 x i32> %a, diff --git a/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll b/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll index b8e8ed0eaf1da..b0b633fba06be 100644 --- a/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll +++ b/llvm/test/Transforms/InstCombine/icmp-equality-xor.ll @@ -136,8 +136,7 @@ define i1 @foo2(i32 %x, i32 %y) { define <2 x i1> @foo3(<2 x i8> %x) { ; CHECK-LABEL: @foo3( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[XOR:%.*]] = xor <2 x i8> [[X:%.*]], -; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i8> [[XOR]], +; CHECK-NEXT: [[CMP:%.*]] = icmp ne <2 x i8> [[X:%.*]], ; CHECK-NEXT: ret <2 x i1> [[CMP]] ; entry: diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index aafb85395f975..c909673481bb4 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -632,12 +632,10 @@ define i1 @icmp_slt_select(i1 %cond, i32 %a, i32 %b) { define i1 @discr_eq(i8 %a, i8 %b) { ; CHECK-LABEL: @discr_eq( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 -; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 -; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 -; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 -; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 -; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3 ; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]] ; CHECK-NEXT: ret i1 [[RES]] ; @@ -655,12 +653,10 @@ entry: define i1 @discr_ne(i8 %a, i8 %b) { ; CHECK-LABEL: @discr_ne( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 -; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 -; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 -; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 -; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 -; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[ADD2]], i8 1 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3 ; CHECK-NEXT: [[RES:%.*]] = icmp ne i8 [[SEL1]], [[SEL2]] ; CHECK-NEXT: ret i1 [[RES]] ; @@ -678,12 +674,10 @@ entry: define i1 @discr_xor_eq(i8 %a, i8 %b) { ; CHECK-LABEL: @discr_xor_eq( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[XOR1:%.*]] = xor i8 [[A:%.*]], -3 -; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 -; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[XOR1]], i8 1 -; CHECK-NEXT: [[XOR2:%.*]] = xor i8 [[B:%.*]], -3 -; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B]], 1 -; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[XOR2]], i8 1 +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 +; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1 +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 -4 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 -4 ; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] ; CHECK-NEXT: ret i1 [[RES]] ; @@ -701,11 +695,9 @@ entry: define i1 @discr_eq_simple(i8 %a, i8 %b) { ; CHECK-LABEL: @discr_eq_simple( ; CHECK-NEXT: entry: -; CHECK-NEXT: [[ADD1:%.*]] = add i8 [[A:%.*]], -2 -; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A]], 1 -; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[ADD1]], i8 1 -; CHECK-NEXT: [[ADD2:%.*]] = add i8 [[B:%.*]], -2 -; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[ADD2]] +; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[ADD2:%.*]] ; CHECK-NEXT: ret i1 [[RES]] ; entry: From c5d67c2f5c1d09d731189dd5e9b1d42ef2329c5b Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Fri, 18 Apr 2025 18:24:31 +0800 Subject: [PATCH 3/9] [InstCombine] Add nopoison check --- .../InstCombine/InstCombineCompares.cpp | 9 ++++++--- llvm/test/Transforms/InstCombine/icmp-select.ll | 17 +++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index aa5da226001a4..90ded7faca2c8 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -5819,13 +5819,16 @@ static void collectOffsetOp(Value *V, SmallVectorImpl &Offsets, switch (Inst->getOpcode()) { case Instruction::Add: - if (match(Inst->getOperand(1), m_ImmConstant(C))) + if (match(Inst->getOperand(1), m_ImmConstant(C)) && + !C->containsUndefOrPoisonElement()) if (Constant *NegC = ConstantExpr::getNeg(C)) Offsets.emplace_back(Instruction::Add, NegC); break; case Instruction::Xor: - Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); - Offsets.emplace_back(Instruction::Xor, Inst->getOperand(0)); + if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) + Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); + if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(0))) + Offsets.emplace_back(Instruction::Xor, Inst->getOperand(0)); break; case Instruction::Select: if (AllowRecursion) { diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index c909673481bb4..4c9535b6cef69 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -758,3 +758,20 @@ entry: %res = icmp eq i8 %sel1, %sel2 ret i1 %res } + +define <2 x i1> @discr_eq_simple_vec(<2 x i8> %a, <2 x i8> %b, i1 %cond) { +; CHECK-LABEL: @discr_eq_simple_vec( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[ADD1:%.*]] = add <2 x i8> [[A:%.*]], +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[COND:%.*]], <2 x i8> [[ADD1]], <2 x i8> splat (i8 1) +; CHECK-NEXT: [[ADD2:%.*]] = add <2 x i8> [[B:%.*]], +; CHECK-NEXT: [[RES:%.*]] = icmp eq <2 x i8> [[SEL1]], [[ADD2]] +; CHECK-NEXT: ret <2 x i1> [[RES]] +; +entry: + %add1 = add <2 x i8> %a, + %sel1 = select i1 %cond, <2 x i8> %add1, <2 x i8> splat(i8 1) + %add2 = add <2 x i8> %b, + %res = icmp eq <2 x i8> %sel1, %add2 + ret <2 x i1> %res +} From 4dddcae8f4e01ef21317fe6ad12f01ad947d7975 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Wed, 23 Apr 2025 14:19:08 +0800 Subject: [PATCH 4/9] [InstCombine] Address review comments. NFC. --- .../lib/Transforms/InstCombine/InstCombineCompares.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 90ded7faca2c8..4f0eaf5e25c79 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -5815,15 +5815,17 @@ static void collectOffsetOp(Value *V, SmallVectorImpl &Offsets, Instruction *Inst = dyn_cast(V); if (!Inst) return; - Constant *C; switch (Inst->getOpcode()) { - case Instruction::Add: + case Instruction::Add: { + Constant *C; if (match(Inst->getOperand(1), m_ImmConstant(C)) && - !C->containsUndefOrPoisonElement()) + !C->containsUndefOrPoisonElement()) { if (Constant *NegC = ConstantExpr::getNeg(C)) Offsets.emplace_back(Instruction::Add, NegC); + } break; + } case Instruction::Xor: if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); @@ -5869,8 +5871,6 @@ struct OffsetResult { return V0; case OffsetKind::Select: return Builder.CreateSelect(V0, V1, V2); - default: - llvm_unreachable("Unknown offset result kind"); } } }; From fa9f7a92e00480881bea9f8cd84ed60fb84df6dc Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Mon, 28 Apr 2025 19:52:45 +0800 Subject: [PATCH 5/9] [InstCombine] Address review comments. --- .../InstCombine/InstCombineCompares.cpp | 33 ++++++++----------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 4f0eaf5e25c79..471fdad3fa11d 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -5813,19 +5813,20 @@ using OffsetOp = std::pair; static void collectOffsetOp(Value *V, SmallVectorImpl &Offsets, bool AllowRecursion) { Instruction *Inst = dyn_cast(V); - if (!Inst) + if (!Inst || !Inst->hasOneUse()) return; switch (Inst->getOpcode()) { - case Instruction::Add: { - Constant *C; - if (match(Inst->getOperand(1), m_ImmConstant(C)) && - !C->containsUndefOrPoisonElement()) { - if (Constant *NegC = ConstantExpr::getNeg(C)) - Offsets.emplace_back(Instruction::Add, NegC); - } + case Instruction::Add: + if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) + Offsets.emplace_back(Instruction::Sub, Inst->getOperand(1)); + if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(0))) + Offsets.emplace_back(Instruction::Sub, Inst->getOperand(0)); + break; + case Instruction::Sub: + if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) + Offsets.emplace_back(Instruction::Add, Inst->getOperand(1)); break; - } case Instruction::Xor: if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); @@ -5834,12 +5835,8 @@ static void collectOffsetOp(Value *V, SmallVectorImpl &Offsets, break; case Instruction::Select: if (AllowRecursion) { - Value *TrueV = Inst->getOperand(1); - if (TrueV->hasOneUse()) - collectOffsetOp(TrueV, Offsets, /*AllowRecursion=*/false); - Value *FalseV = Inst->getOperand(2); - if (FalseV->hasOneUse()) - collectOffsetOp(FalseV, Offsets, /*AllowRecursion=*/false); + collectOffsetOp(Inst->getOperand(1), Offsets, /*AllowRecursion=*/false); + collectOffsetOp(Inst->getOperand(2), Offsets, /*AllowRecursion=*/false); } break; default: @@ -5887,10 +5884,8 @@ static Instruction *foldICmpEqualityWithOffset(ICmpInst &I, return nullptr; SmallVector OffsetOps; - if (Op0->hasOneUse()) - collectOffsetOp(Op0, OffsetOps, /*AllowRecursion=*/true); - if (Op1->hasOneUse()) - collectOffsetOp(Op1, OffsetOps, /*AllowRecursion=*/true); + collectOffsetOp(Op0, OffsetOps, /*AllowRecursion=*/true); + collectOffsetOp(Op1, OffsetOps, /*AllowRecursion=*/true); auto ApplyOffsetImpl = [&](Value *V, unsigned BinOpc, Value *RHS) -> Value * { Value *Simplified = simplifyBinOp(BinOpc, V, RHS, SQ); From acb8426ce2aa73f4c8403bae39017f9b79a04d4b Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Mon, 28 Apr 2025 20:01:39 +0800 Subject: [PATCH 6/9] [InstCombine] Add more tests. NFC. --- .../Transforms/InstCombine/icmp-select.ll | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index 4c9535b6cef69..f3ce0f85541c1 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -709,6 +709,41 @@ entry: ret i1 %res } +define i1 @discr_eq_add_commuted(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) { +; CHECK-LABEL: @discr_eq_add_commuted( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[COND1:%.*]], i8 [[B:%.*]], i8 0 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND2:%.*]], i8 [[C:%.*]], i8 [[B]] +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, %b + %sel1 = select i1 %cond1, i8 %add1, i8 %a + %add2 = add i8 %c, %a + %sel2 = select i1 %cond2, i8 %add2, i8 %add1 + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} + +define i1 @discr_eq_sub(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) { +; CHECK-LABEL: @discr_eq_sub( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[COND1:%.*]], i8 [[B:%.*]], i8 0 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND2:%.*]], i8 [[C:%.*]], i8 0 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %neg = sub i8 0, %a + %sub1 = sub i8 %b, %a + %sel1 = select i1 %cond1, i8 %sub1, i8 %neg + %sub2 = sub i8 %c, %a + %sel2 = select i1 %cond2, i8 %sub2, i8 %neg + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} + ; Negative tests define i1 @discr_eq_multi_use(i8 %a, i8 %b) { @@ -775,3 +810,24 @@ entry: %res = icmp eq <2 x i8> %sel1, %add2 ret <2 x i1> %res } + +define i1 @discr_eq_sub_commuted(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) { +; CHECK-LABEL: @discr_eq_sub_commuted( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[NEG:%.*]] = sub i8 0, [[A:%.*]] +; CHECK-NEXT: [[SUB1:%.*]] = sub i8 [[A]], [[B:%.*]] +; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[COND1:%.*]], i8 [[SUB1]], i8 [[NEG]] +; CHECK-NEXT: [[SUB2:%.*]] = sub i8 [[A]], [[C:%.*]] +; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[COND2:%.*]], i8 [[SUB2]], i8 [[NEG]] +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %neg = sub i8 0, %a + %sub1 = sub i8 %a, %b + %sel1 = select i1 %cond1, i8 %sub1, i8 %neg + %sub2 = sub i8 %a, %c + %sel2 = select i1 %cond2, i8 %sub2, i8 %neg + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} From 74ee1651fb731ae140f186b0c8ebcb03def3a2bf Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Mon, 28 Apr 2025 20:03:13 +0800 Subject: [PATCH 7/9] [InstCombine] Regenerate check lines. NFC. --- llvm/test/Transforms/InstCombine/icmp-select.ll | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index f3ce0f85541c1..ef6c149fd6558 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -634,9 +634,9 @@ define i1 @discr_eq(i8 %a, i8 %b) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 ; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1 -; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 -; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3 -; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] ; CHECK-NEXT: ret i1 [[RES]] ; entry: @@ -655,9 +655,9 @@ define i1 @discr_ne(i8 %a, i8 %b) { ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 ; CHECK-NEXT: [[CMP2:%.*]] = icmp ugt i8 [[B:%.*]], 1 -; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 -; CHECK-NEXT: [[SEL2:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3 -; CHECK-NEXT: [[RES:%.*]] = icmp ne i8 [[SEL1]], [[SEL2]] +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[CMP2]], i8 [[B]], i8 3 +; CHECK-NEXT: [[RES:%.*]] = icmp ne i8 [[TMP0]], [[TMP1]] ; CHECK-NEXT: ret i1 [[RES]] ; entry: @@ -696,8 +696,8 @@ define i1 @discr_eq_simple(i8 %a, i8 %b) { ; CHECK-LABEL: @discr_eq_simple( ; CHECK-NEXT: entry: ; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i8 [[A:%.*]], 1 -; CHECK-NEXT: [[SEL1:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 -; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[SEL1]], [[ADD2:%.*]] +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[CMP1]], i8 [[A]], i8 3 +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[B:%.*]] ; CHECK-NEXT: ret i1 [[RES]] ; entry: From 9f0df3a64a33a6840445f646b3bcc497b0e4f45d Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Tue, 29 Apr 2025 13:53:46 +0800 Subject: [PATCH 8/9] [InstCombine] Address review comments. --- .../InstCombine/InstCombineCompares.cpp | 21 +++++++++---------- .../Transforms/InstCombine/icmp-select.ll | 16 ++++++++++++++ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp index 471fdad3fa11d..27d26900de6be 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCompares.cpp @@ -5818,20 +5818,15 @@ static void collectOffsetOp(Value *V, SmallVectorImpl &Offsets, switch (Inst->getOpcode()) { case Instruction::Add: - if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) - Offsets.emplace_back(Instruction::Sub, Inst->getOperand(1)); - if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(0))) - Offsets.emplace_back(Instruction::Sub, Inst->getOperand(0)); + Offsets.emplace_back(Instruction::Sub, Inst->getOperand(1)); + Offsets.emplace_back(Instruction::Sub, Inst->getOperand(0)); break; case Instruction::Sub: - if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) - Offsets.emplace_back(Instruction::Add, Inst->getOperand(1)); + Offsets.emplace_back(Instruction::Add, Inst->getOperand(1)); break; case Instruction::Xor: - if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(1))) - Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); - if (isGuaranteedNotToBeUndefOrPoison(Inst->getOperand(0))) - Offsets.emplace_back(Instruction::Xor, Inst->getOperand(0)); + Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1)); + Offsets.emplace_back(Instruction::Xor, Inst->getOperand(0)); break; case Instruction::Select: if (AllowRecursion) { @@ -5892,7 +5887,11 @@ static Instruction *foldICmpEqualityWithOffset(ICmpInst &I, // Avoid infinite loops by checking if RHS is an identity for the BinOp. if (!Simplified || Simplified == V) return nullptr; - return Simplified; + // Reject constant expressions as they don't simplify things. + if (isa(Simplified) && !match(Simplified, m_ImmConstant())) + return nullptr; + // Check if the transformation introduces poison. + return impliesPoison(RHS, V) ? Simplified : nullptr; }; auto ApplyOffset = [&](Value *V, unsigned BinOpc, diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index ef6c149fd6558..fb439329f7d5f 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -831,3 +831,19 @@ entry: %res = icmp eq i8 %sel1, %sel2 ret i1 %res } + +@g = external global i8 + +; Do not introduce constant expressions. +define i1 @discr_eq_constantexpr(ptr %p) { +; CHECK-LABEL: @discr_eq_constantexpr( +; CHECK-NEXT: [[I:%.*]] = ptrtoint ptr [[P:%.*]] to i64 +; CHECK-NEXT: [[SUB:%.*]] = sub i64 [[I]], ptrtoint (ptr @g to i64) +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[SUB]], -1 +; CHECK-NEXT: ret i1 [[CMP]] +; + %i = ptrtoint ptr %p to i64 + %sub = sub i64 %i, ptrtoint (ptr @g to i64) + %cmp = icmp eq i64 %sub, -1 + ret i1 %cmp +} From b478d8640e36ed8552eb3881448e5ec48e48e808 Mon Sep 17 00:00:00 2001 From: Yingwei Zheng Date: Tue, 29 Apr 2025 21:07:29 +0800 Subject: [PATCH 9/9] [InstCombine] Add more tests. NFC. --- llvm/test/Transforms/InstCombine/icmp-select.ll | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/llvm/test/Transforms/InstCombine/icmp-select.ll b/llvm/test/Transforms/InstCombine/icmp-select.ll index fb439329f7d5f..1aae91302dab1 100644 --- a/llvm/test/Transforms/InstCombine/icmp-select.ll +++ b/llvm/test/Transforms/InstCombine/icmp-select.ll @@ -726,6 +726,23 @@ entry: ret i1 %res } +define i1 @discr_eq_add_commuted_implies_poison(i8 %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) { +; CHECK-LABEL: @discr_eq_add_commuted_implies_poison( +; CHECK-NEXT: entry: +; CHECK-NEXT: [[TMP0:%.*]] = select i1 [[COND1:%.*]], i8 [[B:%.*]], i8 0 +; CHECK-NEXT: [[TMP1:%.*]] = select i1 [[COND2:%.*]], i8 [[C:%.*]], i8 [[B]] +; CHECK-NEXT: [[RES:%.*]] = icmp eq i8 [[TMP0]], [[TMP1]] +; CHECK-NEXT: ret i1 [[RES]] +; +entry: + %add1 = add i8 %a, %b + %sel1 = select i1 %cond1, i8 %add1, i8 %a + %add2 = add i8 %c, %a + %sel2 = select i1 %cond2, i8 %add2, i8 %add1 + %res = icmp eq i8 %sel1, %sel2 + ret i1 %res +} + define i1 @discr_eq_sub(i8 noundef %a, i8 %b, i8 %c, i1 %cond1, i1 %cond2) { ; CHECK-LABEL: @discr_eq_sub( ; CHECK-NEXT: entry: