Skip to content

Commit

Permalink
[Hexagon] Further improve code generation for shuffles
Browse files Browse the repository at this point in the history
* Concatenate partial shuffles into longer ones whenever possible:
In selection DAG, shuffle's operands and return type must all agree. This
is not the case in LLVM IR, and non-conforming IR-level shuffles will be
rewritten to match DAG's requirements. This can also make a shuffle that
can be matched to a single HVX instruction become shuffles that require
more complex handling. Example: anything that takes two single vectors
and returns a pair (e.g. V6_vshuffvdd).
This is avoided by concatenating such shuffles into ones that take a vector
pair, and an undef pair, and produce a vector pair.

* Recognize perfect shuffles when masks contain `undef` values.

* Use funnel shifts for contracting shuffles.

* Recognize rotations as a separate step.

These changes go into a single commit, because each one on their own
introduced some regressions.
  • Loading branch information
Krzysztof Parzyszek committed Nov 29, 2022
1 parent d4cb392 commit 073d5e5
Show file tree
Hide file tree
Showing 10 changed files with 830 additions and 556 deletions.
21 changes: 12 additions & 9 deletions llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.cpp
Expand Up @@ -682,6 +682,7 @@ void HexagonDAGToDAGISel::SelectIntrinsicWOChain(SDNode *N) {

SDValue V = N->getOperand(1);
SDValue U;
// Splat intrinsics.
if (keepsLowBits(V, Bits, U)) {
SDValue R = CurDAG->getNode(N->getOpcode(), SDLoc(N), N->getValueType(0),
N->getOperand(0), U);
Expand All @@ -697,14 +698,14 @@ void HexagonDAGToDAGISel::SelectExtractSubvector(SDNode *N) {
MVT ResTy = N->getValueType(0).getSimpleVT();
auto IdxN = cast<ConstantSDNode>(N->getOperand(1));
unsigned Idx = IdxN->getZExtValue();
#ifndef NDEBUG
MVT InpTy = Inp.getValueType().getSimpleVT();

[[maybe_unused]] MVT InpTy = Inp.getValueType().getSimpleVT();
[[maybe_unused]] unsigned ResLen = ResTy.getVectorNumElements();
assert(InpTy.getVectorElementType() == ResTy.getVectorElementType());
unsigned ResLen = ResTy.getVectorNumElements();
assert(2 * ResLen == InpTy.getVectorNumElements());
assert(ResTy.getSizeInBits() == 32);
assert(Idx == 0 || Idx == ResLen);
#endif

unsigned SubReg = Idx == 0 ? Hexagon::isub_lo : Hexagon::isub_hi;
SDValue Ext = CurDAG->getTargetExtractSubreg(SubReg, SDLoc(N), ResTy, Inp);

Expand Down Expand Up @@ -904,13 +905,12 @@ void HexagonDAGToDAGISel::Select(SDNode *N) {
return N->setNodeId(-1); // Already selected.

auto isHvxOp = [this](SDNode *N) {
auto &HST = MF->getSubtarget<HexagonSubtarget>();
for (unsigned i = 0, e = N->getNumValues(); i != e; ++i) {
if (HST.isHVXVectorType(N->getValueType(i), true))
if (HST->isHVXVectorType(N->getValueType(i), true))
return true;
}
for (SDValue I : N->ops()) {
if (HST.isHVXVectorType(I.getValueType(), true))
if (HST->isHVXVectorType(I.getValueType(), true))
return true;
}
return false;
Expand Down Expand Up @@ -1258,14 +1258,17 @@ void HexagonDAGToDAGISel::ppHoistZextI1(std::vector<SDNode*> &&Nodes) {
void HexagonDAGToDAGISel::PreprocessISelDAG() {
// Repack all nodes before calling each preprocessing function,
// because each of them can modify the set of nodes.
auto getNodes = [this] () -> std::vector<SDNode*> {
std::vector<SDNode*> T;
auto getNodes = [this]() -> std::vector<SDNode *> {
std::vector<SDNode *> T;
T.reserve(CurDAG->allnodes_size());
for (SDNode &N : CurDAG->allnodes())
T.push_back(&N);
return T;
};

if (HST->useHVXOps())
PreprocessHvxISelDAG();

// Transform: (or (select c x 0) z) -> (select c (or x z) z)
// (or (select c 0 y) z) -> (select c z (or y z))
ppSimplifyOrSelect0(getNodes());
Expand Down
12 changes: 7 additions & 5 deletions llvm/lib/Target/Hexagon/HexagonISelDAGToDAG.h
Expand Up @@ -127,22 +127,24 @@ class HexagonDAGToDAGISel : public SelectionDAGISel {
return SDValue(U, 0);
}

void SelectHvxExtractSubvector(SDNode *N);
void SelectHvxShuffle(SDNode *N);
void SelectHvxRor(SDNode *N);
void SelectHvxVAlign(SDNode *N);

bool keepsLowBits(const SDValue &Val, unsigned NumBits, SDValue &Src);
bool isAlignedMemNode(const MemSDNode *N) const;
bool isSmallStackStore(const StoreSDNode *N) const;
bool isPositiveHalfWord(const SDNode *N) const;
bool hasOneUse(const SDNode *N) const;

// DAG preprocessing functions.
void PreprocessHvxISelDAG();
void ppSimplifyOrSelect0(std::vector<SDNode*> &&Nodes);
void ppAddrReorderAddShl(std::vector<SDNode*> &&Nodes);
void ppAddrRewriteAndSrl(std::vector<SDNode*> &&Nodes);
void ppHoistZextI1(std::vector<SDNode*> &&Nodes);
void ppHvxShuffleOfShuffle(std::vector<SDNode*> &&Nodes);

void SelectHvxExtractSubvector(SDNode *N);
void SelectHvxShuffle(SDNode *N);
void SelectHvxRor(SDNode *N);
void SelectHvxVAlign(SDNode *N);

// Function postprocessing.
void updateAligna();
Expand Down

0 comments on commit 073d5e5

Please sign in to comment.