@@ -4939,6 +4939,96 @@ void LoongArchTargetLowering::ReplaceNodeResults(
4939
4939
}
4940
4940
}
4941
4941
4942
+ // Check if all elements in build_vector are the same or undef, and if so,
4943
+ // return true and set the splat element in SplatValue.
4944
+ static bool isSplatOrUndef (SDNode *N, SDValue &SplatValue) {
4945
+ if (N->getOpcode () != ISD::BUILD_VECTOR)
4946
+ return false ;
4947
+ for (SDValue Op : N->ops ()) {
4948
+ if (!Op.isUndef () && SplatValue && Op != SplatValue)
4949
+ return false ;
4950
+ if (!Op.isUndef ())
4951
+ SplatValue = Op;
4952
+ }
4953
+ return true ;
4954
+ }
4955
+
4956
+ // Helper to attempt to return a cheaper, bit-inverted version of \p V.
4957
+ static SDValue isNOT (SDValue V, SelectionDAG &DAG) {
4958
+ // TODO: don't always ignore oneuse constraints.
4959
+ V = peekThroughBitcasts (V);
4960
+ EVT VT = V.getValueType ();
4961
+
4962
+ // Match not(xor X, -1) -> X.
4963
+ if (V.getOpcode () == ISD::XOR &&
4964
+ (ISD::isBuildVectorAllOnes (V.getOperand (1 ).getNode ()) ||
4965
+ isAllOnesConstant (V.getOperand (1 ))))
4966
+ return V.getOperand (0 );
4967
+
4968
+ // Match not(extract_subvector(not(X)) -> extract_subvector(X).
4969
+ if (V.getOpcode () == ISD::EXTRACT_SUBVECTOR &&
4970
+ (isNullConstant (V.getOperand (1 )) || V.getOperand (0 ).hasOneUse ())) {
4971
+ if (SDValue Not = isNOT (V.getOperand (0 ), DAG)) {
4972
+ Not = DAG.getBitcast (V.getOperand (0 ).getValueType (), Not);
4973
+ return DAG.getNode (ISD::EXTRACT_SUBVECTOR, SDLoc (Not), VT, Not,
4974
+ V.getOperand (1 ));
4975
+ }
4976
+ }
4977
+
4978
+ // Match not(SplatVector(not(X)) -> SplatVector(X).
4979
+ SDValue SplatValue;
4980
+ if (isSplatOrUndef (V.getNode (), SplatValue) &&
4981
+ V->isOnlyUserOf (SplatValue.getNode ())) {
4982
+ if (SDValue Not = isNOT (SplatValue, DAG)) {
4983
+ Not = DAG.getBitcast (V.getOperand (0 ).getValueType (), Not);
4984
+ return DAG.getSplat (VT, SDLoc (Not), Not);
4985
+ }
4986
+ }
4987
+
4988
+ // Match not(or(not(X),not(Y))) -> and(X, Y).
4989
+ if (V.getOpcode () == ISD::OR && DAG.getTargetLoweringInfo ().isTypeLegal (VT) &&
4990
+ V.getOperand (0 ).hasOneUse () && V.getOperand (1 ).hasOneUse ()) {
4991
+ // TODO: Handle cases with single NOT operand -> VANDN
4992
+ if (SDValue Op1 = isNOT (V.getOperand (1 ), DAG))
4993
+ if (SDValue Op0 = isNOT (V.getOperand (0 ), DAG))
4994
+ return DAG.getNode (ISD::AND, SDLoc (V), VT, DAG.getBitcast (VT, Op0),
4995
+ DAG.getBitcast (VT, Op1));
4996
+ }
4997
+
4998
+ // TODO: Add more matching patterns. Such as,
4999
+ // not(concat_vectors(not(X), not(Y))) -> concat_vectors(X, Y).
5000
+ // not(slt(C, X)) -> slt(X - 1, C)
5001
+
5002
+ return SDValue ();
5003
+ }
5004
+
5005
+ // / Try to fold: (and (xor X, -1), Y) -> (vandn X, Y).
5006
+ static SDValue combineAndNotIntoVANDN (SDNode *N, const SDLoc &DL,
5007
+ SelectionDAG &DAG) {
5008
+ assert (N->getOpcode () == ISD::AND && " Unexpected opcode combine into ANDN" );
5009
+
5010
+ MVT VT = N->getSimpleValueType (0 );
5011
+ if (!VT.is128BitVector () && !VT.is256BitVector ())
5012
+ return SDValue ();
5013
+
5014
+ SDValue X, Y;
5015
+ SDValue N0 = N->getOperand (0 );
5016
+ SDValue N1 = N->getOperand (1 );
5017
+
5018
+ if (SDValue Not = isNOT (N0, DAG)) {
5019
+ X = Not;
5020
+ Y = N1;
5021
+ } else if (SDValue Not = isNOT (N1, DAG)) {
5022
+ X = Not;
5023
+ Y = N0;
5024
+ } else
5025
+ return SDValue ();
5026
+
5027
+ X = DAG.getBitcast (VT, X);
5028
+ Y = DAG.getBitcast (VT, Y);
5029
+ return DAG.getNode (LoongArchISD::VANDN, DL, VT, X, Y);
5030
+ }
5031
+
4942
5032
static SDValue performANDCombine (SDNode *N, SelectionDAG &DAG,
4943
5033
TargetLowering::DAGCombinerInfo &DCI,
4944
5034
const LoongArchSubtarget &Subtarget) {
@@ -4960,6 +5050,9 @@ static SDValue performANDCombine(SDNode *N, SelectionDAG &DAG,
4960
5050
if (!Subtarget.has32S ())
4961
5051
return SDValue ();
4962
5052
5053
+ if (SDValue R = combineAndNotIntoVANDN (N, DL, DAG))
5054
+ return R;
5055
+
4963
5056
// Op's second operand must be a shifted mask.
4964
5057
if (!(CN = dyn_cast<ConstantSDNode>(SecondOperand)) ||
4965
5058
!isShiftedMask_64 (CN->getZExtValue (), SMIdx, SMLen))
@@ -6628,6 +6721,65 @@ performEXTRACT_VECTOR_ELTCombine(SDNode *N, SelectionDAG &DAG,
6628
6721
return SDValue ();
6629
6722
}
6630
6723
6724
+ // / Do target-specific dag combines on LoongArchISD::VANDN nodes.
6725
+ static SDValue performVANDNCombine (SDNode *N, SelectionDAG &DAG,
6726
+ TargetLowering::DAGCombinerInfo &DCI,
6727
+ const LoongArchSubtarget &Subtarget) {
6728
+ SDValue N0 = N->getOperand (0 );
6729
+ SDValue N1 = N->getOperand (1 );
6730
+ MVT VT = N->getSimpleValueType (0 );
6731
+ SDLoc DL (N);
6732
+
6733
+ // VANDN(undef, x) -> 0
6734
+ // VANDN(x, undef) -> 0
6735
+ if (N0.isUndef () || N1.isUndef ())
6736
+ return DAG.getConstant (0 , DL, VT);
6737
+
6738
+ // VANDN(0, x) -> x
6739
+ if (ISD::isBuildVectorAllZeros (N0.getNode ()))
6740
+ return N1;
6741
+
6742
+ // VANDN(x, 0) -> 0
6743
+ if (ISD::isBuildVectorAllZeros (N1.getNode ()))
6744
+ return DAG.getConstant (0 , DL, VT);
6745
+
6746
+ // VANDN(x, -1) -> NOT(x) -> XOR(x, -1)
6747
+ if (ISD::isBuildVectorAllOnes (N1.getNode ()))
6748
+ return DAG.getNOT (DL, N0, VT);
6749
+
6750
+ // Turn VANDN back to AND if input is inverted.
6751
+ if (SDValue Not = isNOT (N0, DAG))
6752
+ return DAG.getNode (ISD::AND, DL, VT, DAG.getBitcast (VT, Not), N1);
6753
+
6754
+ // Folds for better commutativity:
6755
+ if (N1->hasOneUse ()) {
6756
+ // VANDN(x,NOT(y)) -> AND(NOT(x),NOT(y)) -> NOT(OR(X,Y)).
6757
+ if (SDValue Not = isNOT (N1, DAG))
6758
+ return DAG.getNOT (
6759
+ DL, DAG.getNode (ISD::OR, DL, VT, N0, DAG.getBitcast (VT, Not)), VT);
6760
+
6761
+ // VANDN(x, SplatVector(Imm)) -> AND(NOT(x), NOT(SplatVector(~Imm)))
6762
+ // -> NOT(OR(x, SplatVector(-Imm))
6763
+ // Combination is performed only when VT is v16i8/v32i8, using `vnori.b` to
6764
+ // gain benefits.
6765
+ if (!DCI.isBeforeLegalizeOps () && (VT == MVT::v16i8 || VT == MVT::v32i8)) {
6766
+ SDValue SplatValue;
6767
+ if (isSplatOrUndef (N1.getNode (), SplatValue) &&
6768
+ N1->isOnlyUserOf (SplatValue.getNode ()))
6769
+ if (auto *C = dyn_cast<ConstantSDNode>(SplatValue)) {
6770
+ uint8_t NCVal = static_cast <uint8_t >(~(C->getSExtValue ()));
6771
+ SDValue Not =
6772
+ DAG.getSplat (VT, DL, DAG.getTargetConstant (NCVal, DL, MVT::i8 ));
6773
+ return DAG.getNOT (
6774
+ DL, DAG.getNode (ISD::OR, DL, VT, N0, DAG.getBitcast (VT, Not)),
6775
+ VT);
6776
+ }
6777
+ }
6778
+ }
6779
+
6780
+ return SDValue ();
6781
+ }
6782
+
6631
6783
SDValue LoongArchTargetLowering::PerformDAGCombine (SDNode *N,
6632
6784
DAGCombinerInfo &DCI) const {
6633
6785
SelectionDAG &DAG = DCI.DAG ;
@@ -6663,6 +6815,8 @@ SDValue LoongArchTargetLowering::PerformDAGCombine(SDNode *N,
6663
6815
return performSPLIT_PAIR_F64Combine (N, DAG, DCI, Subtarget);
6664
6816
case ISD::EXTRACT_VECTOR_ELT:
6665
6817
return performEXTRACT_VECTOR_ELTCombine (N, DAG, DCI, Subtarget);
6818
+ case LoongArchISD::VANDN:
6819
+ return performVANDNCombine (N, DAG, DCI, Subtarget);
6666
6820
}
6667
6821
return SDValue ();
6668
6822
}
@@ -7454,6 +7608,7 @@ const char *LoongArchTargetLowering::getTargetNodeName(unsigned Opcode) const {
7454
7608
NODE_NAME_CASE (VPICK_SEXT_ELT)
7455
7609
NODE_NAME_CASE (VPICK_ZEXT_ELT)
7456
7610
NODE_NAME_CASE (VREPLVE)
7611
+ NODE_NAME_CASE (VANDN)
7457
7612
NODE_NAME_CASE (VALL_ZERO)
7458
7613
NODE_NAME_CASE (VANY_ZERO)
7459
7614
NODE_NAME_CASE (VALL_NONZERO)
0 commit comments