|
28 | 28 | #include "llvm/CodeGen/TargetInstrInfo.h"
|
29 | 29 | #include "llvm/CodeGen/TargetLowering.h"
|
30 | 30 | #include "llvm/CodeGen/TargetOpcodes.h"
|
| 31 | +#include "llvm/IR/ConstantRange.h" |
31 | 32 | #include "llvm/IR/DataLayout.h"
|
32 | 33 | #include "llvm/IR/InstrTypes.h"
|
33 | 34 | #include "llvm/Support/Casting.h"
|
34 | 35 | #include "llvm/Support/DivisionByConstantInfo.h"
|
| 36 | +#include "llvm/Support/ErrorHandling.h" |
35 | 37 | #include "llvm/Support/MathExtras.h"
|
36 | 38 | #include "llvm/Target/TargetMachine.h"
|
37 | 39 | #include <cmath>
|
@@ -6651,3 +6653,181 @@ bool CombinerHelper::matchSelect(MachineInstr &MI, BuildFnTy &MatchInfo) {
|
6651 | 6653 |
|
6652 | 6654 | return false;
|
6653 | 6655 | }
|
| 6656 | + |
| 6657 | +/// Fold (icmp Pred1 V1, C1) && (icmp Pred2 V2, C2) |
| 6658 | +/// or (icmp Pred1 V1, C1) || (icmp Pred2 V2, C2) |
| 6659 | +/// into a single comparison using range-based reasoning. |
| 6660 | +/// see InstCombinerImpl::foldAndOrOfICmpsUsingRanges. |
| 6661 | +bool CombinerHelper::tryFoldAndOrOrICmpsUsingRanges(GLogicalBinOp *Logic, |
| 6662 | + BuildFnTy &MatchInfo) { |
| 6663 | + assert(Logic->getOpcode() != TargetOpcode::G_XOR && "unexpected xor"); |
| 6664 | + bool IsAnd = Logic->getOpcode() == TargetOpcode::G_AND; |
| 6665 | + Register DstReg = Logic->getReg(0); |
| 6666 | + Register LHS = Logic->getLHSReg(); |
| 6667 | + Register RHS = Logic->getRHSReg(); |
| 6668 | + unsigned Flags = Logic->getFlags(); |
| 6669 | + |
| 6670 | + // We need an G_ICMP on the LHS register. |
| 6671 | + GICmp *Cmp1 = getOpcodeDef<GICmp>(LHS, MRI); |
| 6672 | + if (!Cmp1) |
| 6673 | + return false; |
| 6674 | + |
| 6675 | + // We need an G_ICMP on the RHS register. |
| 6676 | + GICmp *Cmp2 = getOpcodeDef<GICmp>(RHS, MRI); |
| 6677 | + if (!Cmp2) |
| 6678 | + return false; |
| 6679 | + |
| 6680 | + // We want to fold the icmps. |
| 6681 | + if (!MRI.hasOneNonDBGUse(Cmp1->getReg(0)) || |
| 6682 | + !MRI.hasOneNonDBGUse(Cmp2->getReg(0))) |
| 6683 | + return false; |
| 6684 | + |
| 6685 | + APInt C1; |
| 6686 | + APInt C2; |
| 6687 | + std::optional<ValueAndVReg> MaybeC1 = |
| 6688 | + getIConstantVRegValWithLookThrough(Cmp1->getRHSReg(), MRI); |
| 6689 | + if (!MaybeC1) |
| 6690 | + return false; |
| 6691 | + C1 = MaybeC1->Value; |
| 6692 | + |
| 6693 | + std::optional<ValueAndVReg> MaybeC2 = |
| 6694 | + getIConstantVRegValWithLookThrough(Cmp2->getRHSReg(), MRI); |
| 6695 | + if (!MaybeC2) |
| 6696 | + return false; |
| 6697 | + C2 = MaybeC2->Value; |
| 6698 | + |
| 6699 | + Register R1 = Cmp1->getLHSReg(); |
| 6700 | + Register R2 = Cmp2->getLHSReg(); |
| 6701 | + CmpInst::Predicate Pred1 = Cmp1->getCond(); |
| 6702 | + CmpInst::Predicate Pred2 = Cmp2->getCond(); |
| 6703 | + LLT CmpTy = MRI.getType(Cmp1->getReg(0)); |
| 6704 | + LLT CmpOperandTy = MRI.getType(R1); |
| 6705 | + |
| 6706 | + // We build ands, adds, and constants of type CmpOperandTy. |
| 6707 | + // They must be legal to build. |
| 6708 | + if (!isLegalOrBeforeLegalizer({TargetOpcode::G_AND, CmpOperandTy}) || |
| 6709 | + !isLegalOrBeforeLegalizer({TargetOpcode::G_ADD, CmpOperandTy}) || |
| 6710 | + !isConstantLegalOrBeforeLegalizer(CmpOperandTy)) |
| 6711 | + return false; |
| 6712 | + |
| 6713 | + // Look through add of a constant offset on R1, R2, or both operands. This |
| 6714 | + // allows us to interpret the R + C' < C'' range idiom into a proper range. |
| 6715 | + std::optional<APInt> Offset1; |
| 6716 | + std::optional<APInt> Offset2; |
| 6717 | + if (R1 != R2) { |
| 6718 | + if (GAdd *Add = getOpcodeDef<GAdd>(R1, MRI)) { |
| 6719 | + std::optional<ValueAndVReg> MaybeOffset1 = |
| 6720 | + getIConstantVRegValWithLookThrough(Add->getRHSReg(), MRI); |
| 6721 | + if (MaybeOffset1) { |
| 6722 | + R1 = Add->getLHSReg(); |
| 6723 | + Offset1 = MaybeOffset1->Value; |
| 6724 | + } |
| 6725 | + } |
| 6726 | + if (GAdd *Add = getOpcodeDef<GAdd>(R2, MRI)) { |
| 6727 | + std::optional<ValueAndVReg> MaybeOffset2 = |
| 6728 | + getIConstantVRegValWithLookThrough(Add->getRHSReg(), MRI); |
| 6729 | + if (MaybeOffset2) { |
| 6730 | + R2 = Add->getLHSReg(); |
| 6731 | + Offset2 = MaybeOffset2->Value; |
| 6732 | + } |
| 6733 | + } |
| 6734 | + } |
| 6735 | + |
| 6736 | + if (R1 != R2) |
| 6737 | + return false; |
| 6738 | + |
| 6739 | + // We calculate the icmp ranges including maybe offsets. |
| 6740 | + ConstantRange CR1 = ConstantRange::makeExactICmpRegion( |
| 6741 | + IsAnd ? ICmpInst::getInversePredicate(Pred1) : Pred1, C1); |
| 6742 | + if (Offset1) |
| 6743 | + CR1 = CR1.subtract(*Offset1); |
| 6744 | + |
| 6745 | + ConstantRange CR2 = ConstantRange::makeExactICmpRegion( |
| 6746 | + IsAnd ? ICmpInst::getInversePredicate(Pred2) : Pred2, C2); |
| 6747 | + if (Offset2) |
| 6748 | + CR2 = CR2.subtract(*Offset2); |
| 6749 | + |
| 6750 | + bool CreateMask = false; |
| 6751 | + APInt LowerDiff; |
| 6752 | + std::optional<ConstantRange> CR = CR1.exactUnionWith(CR2); |
| 6753 | + if (!CR) { |
| 6754 | + // We need non-wrapping ranges. |
| 6755 | + if (CR1.isWrappedSet() || CR2.isWrappedSet()) |
| 6756 | + return false; |
| 6757 | + |
| 6758 | + // Check whether we have equal-size ranges that only differ by one bit. |
| 6759 | + // In that case we can apply a mask to map one range onto the other. |
| 6760 | + LowerDiff = CR1.getLower() ^ CR2.getLower(); |
| 6761 | + APInt UpperDiff = (CR1.getUpper() - 1) ^ (CR2.getUpper() - 1); |
| 6762 | + APInt CR1Size = CR1.getUpper() - CR1.getLower(); |
| 6763 | + if (!LowerDiff.isPowerOf2() || LowerDiff != UpperDiff || |
| 6764 | + CR1Size != CR2.getUpper() - CR2.getLower()) |
| 6765 | + return false; |
| 6766 | + |
| 6767 | + CR = CR1.getLower().ult(CR2.getLower()) ? CR1 : CR2; |
| 6768 | + CreateMask = true; |
| 6769 | + } |
| 6770 | + |
| 6771 | + if (IsAnd) |
| 6772 | + CR = CR->inverse(); |
| 6773 | + |
| 6774 | + CmpInst::Predicate NewPred; |
| 6775 | + APInt NewC, Offset; |
| 6776 | + CR->getEquivalentICmp(NewPred, NewC, Offset); |
| 6777 | + |
| 6778 | + // We take the result type of one of the original icmps, CmpTy, for |
| 6779 | + // the to be build icmp. The operand type, CmpOperandTy, is used for |
| 6780 | + // the other instructions and constants to be build. The types of |
| 6781 | + // the parameters and output are the same for add and and. CmpTy |
| 6782 | + // and the type of DstReg might differ. That is why we zext or trunc |
| 6783 | + // the icmp into the destination register. |
| 6784 | + |
| 6785 | + MatchInfo = [=](MachineIRBuilder &B) { |
| 6786 | + if (CreateMask && Offset != 0) { |
| 6787 | + auto TildeLowerDiff = B.buildConstant(CmpOperandTy, ~LowerDiff); |
| 6788 | + auto And = B.buildAnd(CmpOperandTy, R1, TildeLowerDiff); // the mask. |
| 6789 | + auto OffsetC = B.buildConstant(CmpOperandTy, Offset); |
| 6790 | + auto Add = B.buildAdd(CmpOperandTy, And, OffsetC, Flags); |
| 6791 | + auto NewCon = B.buildConstant(CmpOperandTy, NewC); |
| 6792 | + auto ICmp = B.buildICmp(NewPred, CmpTy, Add, NewCon); |
| 6793 | + B.buildZExtOrTrunc(DstReg, ICmp); |
| 6794 | + } else if (CreateMask && Offset == 0) { |
| 6795 | + auto TildeLowerDiff = B.buildConstant(CmpOperandTy, ~LowerDiff); |
| 6796 | + auto And = B.buildAnd(CmpOperandTy, R1, TildeLowerDiff); // the mask. |
| 6797 | + auto NewCon = B.buildConstant(CmpOperandTy, NewC); |
| 6798 | + auto ICmp = B.buildICmp(NewPred, CmpTy, And, NewCon); |
| 6799 | + B.buildZExtOrTrunc(DstReg, ICmp); |
| 6800 | + } else if (!CreateMask && Offset != 0) { |
| 6801 | + auto OffsetC = B.buildConstant(CmpOperandTy, Offset); |
| 6802 | + auto Add = B.buildAdd(CmpOperandTy, R1, OffsetC, Flags); |
| 6803 | + auto NewCon = B.buildConstant(CmpOperandTy, NewC); |
| 6804 | + auto ICmp = B.buildICmp(NewPred, CmpTy, Add, NewCon); |
| 6805 | + B.buildZExtOrTrunc(DstReg, ICmp); |
| 6806 | + } else if (!CreateMask && Offset == 0) { |
| 6807 | + auto NewCon = B.buildConstant(CmpOperandTy, NewC); |
| 6808 | + auto ICmp = B.buildICmp(NewPred, CmpTy, R1, NewCon); |
| 6809 | + B.buildZExtOrTrunc(DstReg, ICmp); |
| 6810 | + } else { |
| 6811 | + llvm_unreachable("unexpected configuration of CreateMask and Offset"); |
| 6812 | + } |
| 6813 | + }; |
| 6814 | + return true; |
| 6815 | +} |
| 6816 | + |
| 6817 | +bool CombinerHelper::matchAnd(MachineInstr &MI, BuildFnTy &MatchInfo) { |
| 6818 | + GAnd *And = cast<GAnd>(&MI); |
| 6819 | + |
| 6820 | + if (tryFoldAndOrOrICmpsUsingRanges(And, MatchInfo)) |
| 6821 | + return true; |
| 6822 | + |
| 6823 | + return false; |
| 6824 | +} |
| 6825 | + |
| 6826 | +bool CombinerHelper::matchOr(MachineInstr &MI, BuildFnTy &MatchInfo) { |
| 6827 | + GOr *Or = cast<GOr>(&MI); |
| 6828 | + |
| 6829 | + if (tryFoldAndOrOrICmpsUsingRanges(Or, MatchInfo)) |
| 6830 | + return true; |
| 6831 | + |
| 6832 | + return false; |
| 6833 | +} |
0 commit comments