@@ -5808,6 +5808,128 @@ static Instruction *foldICmpPow2Test(ICmpInst &I,
5808
5808
return nullptr;
5809
5809
}
5810
5810
5811
+ /// Find all possible pairs (BinOp, RHS) that BinOp V, RHS can be simplified.
5812
+ using OffsetOp = std::pair<Instruction::BinaryOps, Value *>;
5813
+ static void collectOffsetOp(Value *V, SmallVectorImpl<OffsetOp> &Offsets,
5814
+ bool AllowRecursion) {
5815
+ Instruction *Inst = dyn_cast<Instruction>(V);
5816
+ if (!Inst || !Inst->hasOneUse())
5817
+ return;
5818
+
5819
+ switch (Inst->getOpcode()) {
5820
+ case Instruction::Add:
5821
+ Offsets.emplace_back(Instruction::Sub, Inst->getOperand(1));
5822
+ Offsets.emplace_back(Instruction::Sub, Inst->getOperand(0));
5823
+ break;
5824
+ case Instruction::Sub:
5825
+ Offsets.emplace_back(Instruction::Add, Inst->getOperand(1));
5826
+ break;
5827
+ case Instruction::Xor:
5828
+ Offsets.emplace_back(Instruction::Xor, Inst->getOperand(1));
5829
+ Offsets.emplace_back(Instruction::Xor, Inst->getOperand(0));
5830
+ break;
5831
+ case Instruction::Select:
5832
+ if (AllowRecursion) {
5833
+ collectOffsetOp(Inst->getOperand(1), Offsets, /*AllowRecursion=*/false);
5834
+ collectOffsetOp(Inst->getOperand(2), Offsets, /*AllowRecursion=*/false);
5835
+ }
5836
+ break;
5837
+ default:
5838
+ break;
5839
+ }
5840
+ }
5841
+
5842
+ enum class OffsetKind { Invalid, Value, Select };
5843
+
5844
+ struct OffsetResult {
5845
+ OffsetKind Kind;
5846
+ Value *V0, *V1, *V2;
5847
+
5848
+ static OffsetResult invalid() {
5849
+ return {OffsetKind::Invalid, nullptr, nullptr, nullptr};
5850
+ }
5851
+ static OffsetResult value(Value *V) {
5852
+ return {OffsetKind::Value, V, nullptr, nullptr};
5853
+ }
5854
+ static OffsetResult select(Value *Cond, Value *TrueV, Value *FalseV) {
5855
+ return {OffsetKind::Select, Cond, TrueV, FalseV};
5856
+ }
5857
+ bool isValid() const { return Kind != OffsetKind::Invalid; }
5858
+ Value *materialize(InstCombiner::BuilderTy &Builder) const {
5859
+ switch (Kind) {
5860
+ case OffsetKind::Invalid:
5861
+ llvm_unreachable("Invalid offset result");
5862
+ case OffsetKind::Value:
5863
+ return V0;
5864
+ case OffsetKind::Select:
5865
+ return Builder.CreateSelect(V0, V1, V2);
5866
+ }
5867
+ }
5868
+ };
5869
+
5870
+ /// Offset both sides of an equality icmp to see if we can save some
5871
+ /// instructions: icmp eq/ne X, Y -> icmp eq/ne X op Z, Y op Z.
5872
+ /// Note: This operation should not introduce poison.
5873
+ static Instruction *foldICmpEqualityWithOffset(ICmpInst &I,
5874
+ InstCombiner::BuilderTy &Builder,
5875
+ const SimplifyQuery &SQ) {
5876
+ assert(I.isEquality() && "Expected an equality icmp");
5877
+ Value *Op0 = I.getOperand(0), *Op1 = I.getOperand(1);
5878
+ if (!Op0->getType()->isIntOrIntVectorTy())
5879
+ return nullptr;
5880
+
5881
+ SmallVector<OffsetOp, 4> OffsetOps;
5882
+ collectOffsetOp(Op0, OffsetOps, /*AllowRecursion=*/true);
5883
+ collectOffsetOp(Op1, OffsetOps, /*AllowRecursion=*/true);
5884
+
5885
+ auto ApplyOffsetImpl = [&](Value *V, unsigned BinOpc, Value *RHS) -> Value * {
5886
+ Value *Simplified = simplifyBinOp(BinOpc, V, RHS, SQ);
5887
+ // Avoid infinite loops by checking if RHS is an identity for the BinOp.
5888
+ if (!Simplified || Simplified == V)
5889
+ return nullptr;
5890
+ // Reject constant expressions as they don't simplify things.
5891
+ if (isa<Constant>(Simplified) && !match(Simplified, m_ImmConstant()))
5892
+ return nullptr;
5893
+ // Check if the transformation introduces poison.
5894
+ return impliesPoison(RHS, V) ? Simplified : nullptr;
5895
+ };
5896
+
5897
+ auto ApplyOffset = [&](Value *V, unsigned BinOpc,
5898
+ Value *RHS) -> OffsetResult {
5899
+ if (auto *Sel = dyn_cast<SelectInst>(V)) {
5900
+ if (!Sel->hasOneUse())
5901
+ return OffsetResult::invalid();
5902
+ Value *TrueVal = ApplyOffsetImpl(Sel->getTrueValue(), BinOpc, RHS);
5903
+ if (!TrueVal)
5904
+ return OffsetResult::invalid();
5905
+ Value *FalseVal = ApplyOffsetImpl(Sel->getFalseValue(), BinOpc, RHS);
5906
+ if (!FalseVal)
5907
+ return OffsetResult::invalid();
5908
+ return OffsetResult::select(Sel->getCondition(), TrueVal, FalseVal);
5909
+ }
5910
+ if (Value *Simplified = ApplyOffsetImpl(V, BinOpc, RHS))
5911
+ return OffsetResult::value(Simplified);
5912
+ return OffsetResult::invalid();
5913
+ };
5914
+
5915
+ for (auto [BinOp, RHS] : OffsetOps) {
5916
+ auto BinOpc = static_cast<unsigned>(BinOp);
5917
+
5918
+ auto Op0Result = ApplyOffset(Op0, BinOpc, RHS);
5919
+ if (!Op0Result.isValid())
5920
+ continue;
5921
+ auto Op1Result = ApplyOffset(Op1, BinOpc, RHS);
5922
+ if (!Op1Result.isValid())
5923
+ continue;
5924
+
5925
+ Value *NewLHS = Op0Result.materialize(Builder);
5926
+ Value *NewRHS = Op1Result.materialize(Builder);
5927
+ return new ICmpInst(I.getPredicate(), NewLHS, NewRHS);
5928
+ }
5929
+
5930
+ return nullptr;
5931
+ }
5932
+
5811
5933
Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) {
5812
5934
if (!I.isEquality())
5813
5935
return nullptr;
@@ -6054,6 +6176,10 @@ Instruction *InstCombinerImpl::foldICmpEquality(ICmpInst &I) {
6054
6176
: ConstantInt::getNullValue(A->getType()));
6055
6177
}
6056
6178
6179
+ if (auto *Res = foldICmpEqualityWithOffset(
6180
+ I, Builder, getSimplifyQuery().getWithInstruction(&I)))
6181
+ return Res;
6182
+
6057
6183
return nullptr;
6058
6184
}
6059
6185
0 commit comments