diff --git a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h --- a/llvm/include/llvm/CodeGen/SelectionDAGNodes.h +++ b/llvm/include/llvm/CodeGen/SelectionDAGNodes.h @@ -1708,7 +1708,7 @@ /// Return true if the value is a constant -1 integer or a splatted vector of a /// constant -1 integer (with no undefs). /// Does not permit build vector implicit truncation. -bool isAllOnesOrAllOnesSplat(SDValue V); +bool isAllOnesOrAllOnesSplat(SDValue V, bool AllowTruncation = false); class GlobalAddressSDNode : public SDNode { friend class SelectionDAG; diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAG.cpp @@ -8789,11 +8789,18 @@ return C && C->isOne() && C->getValueSizeInBits(0) == BitWidth; } -bool llvm::isAllOnesOrAllOnesSplat(SDValue N) { +bool llvm::isAllOnesOrAllOnesSplat(SDValue N, bool AllowTruncation) { N = peekThroughBitcasts(N); unsigned BitWidth = N.getScalarValueSizeInBits(); - ConstantSDNode *C = isConstOrConstSplat(N); - return C && C->isAllOnesValue() && C->getValueSizeInBits(0) == BitWidth; + ConstantSDNode *C = + isConstOrConstSplat(N, /*AllowUndefs=*/false, AllowTruncation); + if (!C) + return false; + if (!AllowTruncation) + return C->isAllOnesValue() && C->getValueSizeInBits(0) == BitWidth; + return C->getAPIntValue() + .zextOrTrunc(N.getScalarValueSizeInBits()) + .isAllOnesValue(); } HandleSDNode::~HandleSDNode() { diff --git a/llvm/lib/Target/Mips/MipsISelDAGToDAG.h b/llvm/lib/Target/Mips/MipsISelDAGToDAG.h --- a/llvm/lib/Target/Mips/MipsISelDAGToDAG.h +++ b/llvm/lib/Target/Mips/MipsISelDAGToDAG.h @@ -125,6 +125,10 @@ /// starting at bit zero. virtual bool selectVSplatMaskR(SDValue N, SDValue &Imm) const; + /// Convert vector increment with all-ones constant to sub with an one-const: + /// add X, <-1, -1...> --> sub X, <1, 1...> + bool selectVectorSubOne(SDNode *Node); + void Select(SDNode *N) override; virtual bool trySelect(SDNode *Node) = 0; diff --git a/llvm/lib/Target/Mips/MipsISelDAGToDAG.cpp b/llvm/lib/Target/Mips/MipsISelDAGToDAG.cpp --- a/llvm/lib/Target/Mips/MipsISelDAGToDAG.cpp +++ b/llvm/lib/Target/Mips/MipsISelDAGToDAG.cpp @@ -217,6 +217,28 @@ return false; } +/// Convert vector increment with all-ones constant to sub with an one-constant: +/// add X, <-1, -1...> --> sub X, <1, 1...> +bool MipsDAGToDAGISel::selectVectorSubOne(SDNode *Node) { + assert(Node->getOpcode() == ISD::ADD && "Should only get 'add' here."); + + EVT VT = Node->getValueType(0); + assert(VT.isVector() && "Should only be called for vectors."); + + SDValue X = Node->getOperand(0); + if (!isAllOnesOrAllOnesSplat(Node->getOperand(1), /*AllowTruncation=*/true)) + return false; + + SDLoc DL(Node); + APInt One(VT.getScalarSizeInBits(), 1); + SDValue OnesVec = CurDAG->getConstant(One, DL, VT); + SDValue NewNode = CurDAG->getNode(ISD::SUB, DL, VT, X, OnesVec); + + ReplaceNode(Node, NewNode.getNode()); + SelectCode(NewNode.getNode()); + return true; +} + /// Select instructions not customized! Used for /// expanded, promoted and normal instructions void MipsDAGToDAGISel::Select(SDNode *Node) { @@ -236,6 +258,11 @@ switch(Opcode) { default: break; + case ISD::ADD: + if (Node->getSimpleValueType(0).isVector() && selectVectorSubOne(Node)) + return; + break; + // Get target GOT address. case ISD::GLOBAL_OFFSET_TABLE: ReplaceNode(Node, getGlobalBaseReg()); diff --git a/llvm/test/CodeGen/Mips/msa/arithmetic.ll b/llvm/test/CodeGen/Mips/msa/arithmetic.ll --- a/llvm/test/CodeGen/Mips/msa/arithmetic.ll +++ b/llvm/test/CodeGen/Mips/msa/arithmetic.ll @@ -180,8 +180,7 @@ ; ALL-LABEL: sub_v16i8_i: ; ALL: # %bb.0: ; ALL-NEXT: ld.b $w0, 0($5) -; ALL-NEXT: ldi.b $w1, -1 -; ALL-NEXT: addv.b $w0, $w0, $w1 +; ALL-NEXT: subvi.b $w0, $w0, 1 ; ALL-NEXT: jr $ra ; ALL-NEXT: st.b $w0, 0($4) %1 = load <16 x i8>, <16 x i8>* %a @@ -194,9 +193,8 @@ define void @sub_v8i16_i(<8 x i16>* %c, <8 x i16>* %a) nounwind { ; ALL-LABEL: sub_v8i16_i: ; ALL: # %bb.0: -; ALL-NEXT: ldi.b $w0, -1 -; ALL-NEXT: ld.h $w1, 0($5) -; ALL-NEXT: addv.h $w0, $w1, $w0 +; ALL-NEXT: ld.h $w0, 0($5) +; ALL-NEXT: subvi.h $w0, $w0, 1 ; ALL-NEXT: jr $ra ; ALL-NEXT: st.h $w0, 0($4) %1 = load <8 x i16>, <8 x i16>* %a @@ -209,9 +207,8 @@ define void @sub_v4i32_i(<4 x i32>* %c, <4 x i32>* %a) nounwind { ; ALL-LABEL: sub_v4i32_i: ; ALL: # %bb.0: -; ALL-NEXT: ldi.b $w0, -1 -; ALL-NEXT: ld.w $w1, 0($5) -; ALL-NEXT: addv.w $w0, $w1, $w0 +; ALL-NEXT: ld.w $w0, 0($5) +; ALL-NEXT: subvi.w $w0, $w0, 1 ; ALL-NEXT: jr $ra ; ALL-NEXT: st.w $w0, 0($4) %1 = load <4 x i32>, <4 x i32>* %a @@ -221,22 +218,12 @@ } define void @sub_v2i64_i(<2 x i64>* %c, <2 x i64>* %a) nounwind { -; MIPS-LABEL: sub_v2i64_i: -; MIPS: # %bb.0: -; MIPS-NEXT: ldi.b $w0, -1 -; MIPS-NEXT: shf.w $w0, $w0, 177 -; MIPS-NEXT: ld.d $w1, 0($5) -; MIPS-NEXT: addv.d $w0, $w1, $w0 -; MIPS-NEXT: jr $ra -; MIPS-NEXT: st.d $w0, 0($4) -; -; MIPSEL-LABEL: sub_v2i64_i: -; MIPSEL: # %bb.0: -; MIPSEL-NEXT: ldi.b $w0, -1 -; MIPSEL-NEXT: ld.d $w1, 0($5) -; MIPSEL-NEXT: addv.d $w0, $w1, $w0 -; MIPSEL-NEXT: jr $ra -; MIPSEL-NEXT: st.d $w0, 0($4) +; ALL-LABEL: sub_v2i64_i: +; ALL: # %bb.0: +; ALL-NEXT: ld.d $w0, 0($5) +; ALL-NEXT: subvi.d $w0, $w0, 1 +; ALL-NEXT: jr $ra +; ALL-NEXT: st.d $w0, 0($4) %1 = load <2 x i64>, <2 x i64>* %a %2 = sub <2 x i64> %1, store <2 x i64> %2, <2 x i64>* %c