-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[RISCV] Exploit register boundaries when lowering shuffle with exact vlen #79072
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4650,6 +4650,88 @@ static SDValue lowerVECTOR_SHUFFLEAsRotate(ShuffleVectorSDNode *SVN, | |
return DAG.getBitcast(VT, Rotate); | ||
} | ||
|
||
// If compiling with an exactly known VLEN, see if we can split a | ||
// shuffle on m2 or larger into a small number of m1 sized shuffles | ||
// which write each destination registers exactly once. | ||
static SDValue lowerShuffleViaVRegSplitting(ShuffleVectorSDNode *SVN, | ||
SelectionDAG &DAG, | ||
const RISCVSubtarget &Subtarget) { | ||
SDLoc DL(SVN); | ||
MVT VT = SVN->getSimpleValueType(0); | ||
SDValue V1 = SVN->getOperand(0); | ||
SDValue V2 = SVN->getOperand(1); | ||
ArrayRef<int> Mask = SVN->getMask(); | ||
unsigned NumElts = VT.getVectorNumElements(); | ||
|
||
// If we don't know exact data layout, not much we can do. If this | ||
// is already m1 or smaller, no point in splitting further. | ||
const unsigned MinVLen = Subtarget.getRealMinVLen(); | ||
const unsigned MaxVLen = Subtarget.getRealMaxVLen(); | ||
if (MinVLen != MaxVLen || | ||
VT.getSizeInBits().getKnownMinValue() <= MinVLen) | ||
return SDValue(); | ||
|
||
MVT ElemVT = VT.getVectorElementType(); | ||
unsigned ElemsPerVReg = MinVLen / ElemVT.getFixedSizeInBits(); | ||
unsigned VRegsPerSrc = NumElts / ElemsPerVReg; | ||
|
||
SmallVector<std::pair<int, SmallVector<int>>> OutMasks; | ||
OutMasks.resize(VRegsPerSrc); | ||
for (unsigned i = 0; i < OutMasks.size(); i++) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there some way to do this from the constructor? The |
||
OutMasks[i].first = -1; | ||
|
||
// Check if our mask can be done as a 1-to-1 mapping from source | ||
// to destination registers in the group without needing to | ||
// write each destination more than once. | ||
for (unsigned DstIdx = 0; DstIdx < Mask.size(); DstIdx++) { | ||
int DstVecIdx = DstIdx / ElemsPerVReg; | ||
int DstSubIdx = DstIdx % ElemsPerVReg; | ||
int SrcIdx = Mask[DstIdx]; | ||
if (SrcIdx < 0 || (unsigned)SrcIdx >= 2 * NumElts) | ||
continue; | ||
int SrcVecIdx = SrcIdx / ElemsPerVReg; | ||
int SrcSubIdx = SrcIdx % ElemsPerVReg; | ||
if (OutMasks[DstVecIdx].first == -1) | ||
OutMasks[DstVecIdx].first = SrcVecIdx; | ||
if (OutMasks[DstVecIdx].first != SrcVecIdx) | ||
// Note: This case could easily be handled by keeping track of a chain | ||
// of source values and generating two element shuffles below. This is | ||
// less an implementation question, and more a profitability one. | ||
return SDValue(); | ||
|
||
OutMasks[DstVecIdx].second.resize(ElemsPerVReg); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If a src indx is -1, does this lose that knowledge? Should this file with -1? |
||
OutMasks[DstVecIdx].second[DstSubIdx] = SrcSubIdx; | ||
} | ||
|
||
EVT ContainerVT = getContainerForFixedLengthVector(DAG, VT, Subtarget); | ||
MVT OneRegVT = MVT::getVectorVT(ElemVT, ElemsPerVReg); | ||
MVT M1VT = getContainerForFixedLengthVector(DAG, OneRegVT, Subtarget); | ||
assert(M1VT == getLMUL1VT(M1VT)); | ||
unsigned NumOpElts = M1VT.getVectorMinNumElements(); | ||
SDValue Vec = DAG.getUNDEF(ContainerVT); | ||
// The following semantically builds up a fixed length concat_vector | ||
// of the component shuffle_vectors. We eagerly lower to scalable here | ||
// to avoid DAG combining it back to a large shuffle_vector again. | ||
V1 = convertToScalableVector(ContainerVT, V1, DAG, Subtarget); | ||
V2 = convertToScalableVector(ContainerVT, V2, DAG, Subtarget); | ||
for (unsigned DstVecIdx = 0 ; DstVecIdx < OutMasks.size(); DstVecIdx++) { | ||
auto &[SrcVecIdx, SrcSubMask] = OutMasks[DstVecIdx]; | ||
if (SrcVecIdx == -1) | ||
continue; | ||
unsigned ExtractIdx = (SrcVecIdx % VRegsPerSrc) * NumOpElts; | ||
SDValue SrcVec = (unsigned)SrcVecIdx > VRegsPerSrc ? V2 : V1; | ||
SDValue SubVec = DAG.getNode(ISD::EXTRACT_SUBVECTOR, DL, M1VT, SrcVec, | ||
DAG.getVectorIdxConstant(ExtractIdx, DL)); | ||
SubVec = convertFromScalableVector(OneRegVT, SubVec, DAG, Subtarget); | ||
SubVec = DAG.getVectorShuffle(OneRegVT, DL, SubVec, SubVec, SrcSubMask); | ||
SubVec = convertToScalableVector(M1VT, SubVec, DAG, Subtarget); | ||
unsigned InsertIdx = DstVecIdx * NumOpElts; | ||
Vec = DAG.getNode(ISD::INSERT_SUBVECTOR, DL, ContainerVT, Vec, SubVec, | ||
DAG.getVectorIdxConstant(InsertIdx, DL)); | ||
} | ||
return convertFromScalableVector(VT, Vec, DAG, Subtarget); | ||
} | ||
|
||
static SDValue lowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG, | ||
const RISCVSubtarget &Subtarget) { | ||
SDValue V1 = Op.getOperand(0); | ||
|
@@ -4757,6 +4839,11 @@ static SDValue lowerVECTOR_SHUFFLE(SDValue Op, SelectionDAG &DAG, | |
} | ||
} | ||
|
||
// For exact VLEN m2 or greater, try to split to m1 operations if we | ||
// can split cleanly. | ||
if (SDValue V = lowerShuffleViaVRegSplitting(SVN, DAG, Subtarget)) | ||
return V; | ||
|
||
ArrayRef<int> Mask = SVN->getMask(); | ||
|
||
if (SDValue V = | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getKnownMinValue() can be getFixedValue() I think?