444 lines
15 KiB
C++
444 lines
15 KiB
C++
//===-- VVPISelLowering.cpp - VE DAG Lowering Implementation --------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the lowering and legalization of vector instructions to
|
|
// VVP_*layer SDNodes.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "VECustomDAG.h"
|
|
#include "VEISelLowering.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define DEBUG_TYPE "ve-lower"
|
|
|
|
SDValue VETargetLowering::splitMaskArithmetic(SDValue Op,
|
|
SelectionDAG &DAG) const {
|
|
VECustomDAG CDAG(DAG, Op);
|
|
SDValue AVL =
|
|
CDAG.getConstant(Op.getValueType().getVectorNumElements(), MVT::i32);
|
|
SDValue A = Op->getOperand(0);
|
|
SDValue B = Op->getOperand(1);
|
|
SDValue LoA = CDAG.getUnpack(MVT::v256i1, A, PackElem::Lo, AVL);
|
|
SDValue HiA = CDAG.getUnpack(MVT::v256i1, A, PackElem::Hi, AVL);
|
|
SDValue LoB = CDAG.getUnpack(MVT::v256i1, B, PackElem::Lo, AVL);
|
|
SDValue HiB = CDAG.getUnpack(MVT::v256i1, B, PackElem::Hi, AVL);
|
|
unsigned Opc = Op.getOpcode();
|
|
auto LoRes = CDAG.getNode(Opc, MVT::v256i1, {LoA, LoB});
|
|
auto HiRes = CDAG.getNode(Opc, MVT::v256i1, {HiA, HiB});
|
|
return CDAG.getPack(MVT::v512i1, LoRes, HiRes, AVL);
|
|
}
|
|
|
|
SDValue VETargetLowering::lowerToVVP(SDValue Op, SelectionDAG &DAG) const {
|
|
// Can we represent this as a VVP node.
|
|
const unsigned Opcode = Op->getOpcode();
|
|
auto VVPOpcodeOpt = getVVPOpcode(Opcode);
|
|
if (!VVPOpcodeOpt)
|
|
return SDValue();
|
|
unsigned VVPOpcode = VVPOpcodeOpt.value();
|
|
const bool FromVP = ISD::isVPOpcode(Opcode);
|
|
|
|
// The representative and legalized vector type of this operation.
|
|
VECustomDAG CDAG(DAG, Op);
|
|
// Dispatch to complex lowering functions.
|
|
switch (VVPOpcode) {
|
|
case VEISD::VVP_LOAD:
|
|
case VEISD::VVP_STORE:
|
|
return lowerVVP_LOAD_STORE(Op, CDAG);
|
|
case VEISD::VVP_GATHER:
|
|
case VEISD::VVP_SCATTER:
|
|
return lowerVVP_GATHER_SCATTER(Op, CDAG);
|
|
}
|
|
|
|
EVT OpVecVT = *getIdiomaticVectorType(Op.getNode());
|
|
EVT LegalVecVT = getTypeToTransformTo(*DAG.getContext(), OpVecVT);
|
|
auto Packing = getTypePacking(LegalVecVT.getSimpleVT());
|
|
|
|
SDValue AVL;
|
|
SDValue Mask;
|
|
|
|
if (FromVP) {
|
|
// All upstream VP SDNodes always have a mask and avl.
|
|
auto MaskIdx = ISD::getVPMaskIdx(Opcode);
|
|
auto AVLIdx = ISD::getVPExplicitVectorLengthIdx(Opcode);
|
|
if (MaskIdx)
|
|
Mask = Op->getOperand(*MaskIdx);
|
|
if (AVLIdx)
|
|
AVL = Op->getOperand(*AVLIdx);
|
|
}
|
|
|
|
// Materialize default mask and avl.
|
|
if (!AVL)
|
|
AVL = CDAG.getConstant(OpVecVT.getVectorNumElements(), MVT::i32);
|
|
if (!Mask)
|
|
Mask = CDAG.getConstantMask(Packing, true);
|
|
|
|
assert(LegalVecVT.isSimple());
|
|
if (isVVPUnaryOp(VVPOpcode))
|
|
return CDAG.getNode(VVPOpcode, LegalVecVT, {Op->getOperand(0), Mask, AVL});
|
|
if (isVVPBinaryOp(VVPOpcode))
|
|
return CDAG.getNode(VVPOpcode, LegalVecVT,
|
|
{Op->getOperand(0), Op->getOperand(1), Mask, AVL});
|
|
if (isVVPReductionOp(VVPOpcode)) {
|
|
auto SrcHasStart = hasReductionStartParam(Op->getOpcode());
|
|
SDValue StartV = SrcHasStart ? Op->getOperand(0) : SDValue();
|
|
SDValue VectorV = Op->getOperand(SrcHasStart ? 1 : 0);
|
|
return CDAG.getLegalReductionOpVVP(VVPOpcode, Op.getValueType(), StartV,
|
|
VectorV, Mask, AVL, Op->getFlags());
|
|
}
|
|
|
|
switch (VVPOpcode) {
|
|
default:
|
|
llvm_unreachable("lowerToVVP called for unexpected SDNode.");
|
|
case VEISD::VVP_FFMA: {
|
|
// VE has a swizzled operand order in FMA (compared to LLVM IR and
|
|
// SDNodes).
|
|
auto X = Op->getOperand(2);
|
|
auto Y = Op->getOperand(0);
|
|
auto Z = Op->getOperand(1);
|
|
return CDAG.getNode(VVPOpcode, LegalVecVT, {X, Y, Z, Mask, AVL});
|
|
}
|
|
case VEISD::VVP_SELECT: {
|
|
auto Mask = Op->getOperand(0);
|
|
auto OnTrue = Op->getOperand(1);
|
|
auto OnFalse = Op->getOperand(2);
|
|
return CDAG.getNode(VVPOpcode, LegalVecVT, {OnTrue, OnFalse, Mask, AVL});
|
|
}
|
|
case VEISD::VVP_SETCC: {
|
|
EVT LegalResVT = getTypeToTransformTo(*DAG.getContext(), Op.getValueType());
|
|
auto LHS = Op->getOperand(0);
|
|
auto RHS = Op->getOperand(1);
|
|
auto Pred = Op->getOperand(2);
|
|
return CDAG.getNode(VVPOpcode, LegalResVT, {LHS, RHS, Pred, Mask, AVL});
|
|
}
|
|
}
|
|
}
|
|
|
|
SDValue VETargetLowering::lowerVVP_LOAD_STORE(SDValue Op,
|
|
VECustomDAG &CDAG) const {
|
|
auto VVPOpc = *getVVPOpcode(Op->getOpcode());
|
|
const bool IsLoad = (VVPOpc == VEISD::VVP_LOAD);
|
|
|
|
// Shares.
|
|
SDValue BasePtr = getMemoryPtr(Op);
|
|
SDValue Mask = getNodeMask(Op);
|
|
SDValue Chain = getNodeChain(Op);
|
|
SDValue AVL = getNodeAVL(Op);
|
|
// Store specific.
|
|
SDValue Data = getStoredValue(Op);
|
|
// Load specific.
|
|
SDValue PassThru = getNodePassthru(Op);
|
|
|
|
SDValue StrideV = getLoadStoreStride(Op, CDAG);
|
|
|
|
auto DataVT = *getIdiomaticVectorType(Op.getNode());
|
|
auto Packing = getTypePacking(DataVT);
|
|
|
|
// TODO: Infer lower AVL from mask.
|
|
if (!AVL)
|
|
AVL = CDAG.getConstant(DataVT.getVectorNumElements(), MVT::i32);
|
|
|
|
// Default to the all-true mask.
|
|
if (!Mask)
|
|
Mask = CDAG.getConstantMask(Packing, true);
|
|
|
|
if (IsLoad) {
|
|
MVT LegalDataVT = getLegalVectorType(
|
|
Packing, DataVT.getVectorElementType().getSimpleVT());
|
|
|
|
auto NewLoadV = CDAG.getNode(VEISD::VVP_LOAD, {LegalDataVT, MVT::Other},
|
|
{Chain, BasePtr, StrideV, Mask, AVL});
|
|
|
|
if (!PassThru || PassThru->isUndef())
|
|
return NewLoadV;
|
|
|
|
// Convert passthru to an explicit select node.
|
|
SDValue DataV = CDAG.getNode(VEISD::VVP_SELECT, DataVT,
|
|
{NewLoadV, PassThru, Mask, AVL});
|
|
SDValue NewLoadChainV = SDValue(NewLoadV.getNode(), 1);
|
|
|
|
// Merge them back into one node.
|
|
return CDAG.getMergeValues({DataV, NewLoadChainV});
|
|
}
|
|
|
|
// VVP_STORE
|
|
assert(VVPOpc == VEISD::VVP_STORE);
|
|
return CDAG.getNode(VEISD::VVP_STORE, Op.getNode()->getVTList(),
|
|
{Chain, Data, BasePtr, StrideV, Mask, AVL});
|
|
}
|
|
|
|
SDValue VETargetLowering::splitPackedLoadStore(SDValue Op,
|
|
VECustomDAG &CDAG) const {
|
|
auto VVPOC = *getVVPOpcode(Op.getOpcode());
|
|
assert((VVPOC == VEISD::VVP_LOAD) || (VVPOC == VEISD::VVP_STORE));
|
|
|
|
MVT DataVT = getIdiomaticVectorType(Op.getNode())->getSimpleVT();
|
|
assert(getTypePacking(DataVT) == Packing::Dense &&
|
|
"Can only split packed load/store");
|
|
MVT SplitDataVT = splitVectorType(DataVT);
|
|
|
|
assert(!getNodePassthru(Op) &&
|
|
"Should have been folded in lowering to VVP layer");
|
|
|
|
// Analyze the operation
|
|
SDValue PackedMask = getNodeMask(Op);
|
|
SDValue PackedAVL = getAnnotatedNodeAVL(Op).first;
|
|
SDValue PackPtr = getMemoryPtr(Op);
|
|
SDValue PackData = getStoredValue(Op);
|
|
SDValue PackStride = getLoadStoreStride(Op, CDAG);
|
|
|
|
unsigned ChainResIdx = PackData ? 0 : 1;
|
|
|
|
SDValue PartOps[2];
|
|
|
|
SDValue UpperPartAVL; // we will use this for packing things back together
|
|
for (PackElem Part : {PackElem::Hi, PackElem::Lo}) {
|
|
// VP ops already have an explicit mask and AVL. When expanding from non-VP
|
|
// attach those additional inputs here.
|
|
auto SplitTM = CDAG.getTargetSplitMask(PackedMask, PackedAVL, Part);
|
|
|
|
// Keep track of the (higher) lvl.
|
|
if (Part == PackElem::Hi)
|
|
UpperPartAVL = SplitTM.AVL;
|
|
|
|
// Attach non-predicating value operands
|
|
SmallVector<SDValue, 4> OpVec;
|
|
|
|
// Chain
|
|
OpVec.push_back(getNodeChain(Op));
|
|
|
|
// Data
|
|
if (PackData) {
|
|
SDValue PartData =
|
|
CDAG.getUnpack(SplitDataVT, PackData, Part, SplitTM.AVL);
|
|
OpVec.push_back(PartData);
|
|
}
|
|
|
|
// Ptr & Stride
|
|
// Push (ptr + ElemBytes * <Part>, 2 * ElemBytes)
|
|
// Stride info
|
|
// EVT DataVT = LegalizeVectorType(getMemoryDataVT(Op), Op, DAG, Mode);
|
|
OpVec.push_back(CDAG.getSplitPtrOffset(PackPtr, PackStride, Part));
|
|
OpVec.push_back(CDAG.getSplitPtrStride(PackStride));
|
|
|
|
// Add predicating args and generate part node
|
|
OpVec.push_back(SplitTM.Mask);
|
|
OpVec.push_back(SplitTM.AVL);
|
|
|
|
if (PackData) {
|
|
// Store
|
|
PartOps[(int)Part] = CDAG.getNode(VVPOC, MVT::Other, OpVec);
|
|
} else {
|
|
// Load
|
|
PartOps[(int)Part] =
|
|
CDAG.getNode(VVPOC, {SplitDataVT, MVT::Other}, OpVec);
|
|
}
|
|
}
|
|
|
|
// Merge the chains
|
|
SDValue LowChain = SDValue(PartOps[(int)PackElem::Lo].getNode(), ChainResIdx);
|
|
SDValue HiChain = SDValue(PartOps[(int)PackElem::Hi].getNode(), ChainResIdx);
|
|
SDValue FusedChains =
|
|
CDAG.getNode(ISD::TokenFactor, MVT::Other, {LowChain, HiChain});
|
|
|
|
// Chain only [store]
|
|
if (PackData)
|
|
return FusedChains;
|
|
|
|
// Re-pack into full packed vector result
|
|
MVT PackedVT =
|
|
getLegalVectorType(Packing::Dense, DataVT.getVectorElementType());
|
|
SDValue PackedVals = CDAG.getPack(PackedVT, PartOps[(int)PackElem::Lo],
|
|
PartOps[(int)PackElem::Hi], UpperPartAVL);
|
|
|
|
return CDAG.getMergeValues({PackedVals, FusedChains});
|
|
}
|
|
|
|
SDValue VETargetLowering::lowerVVP_GATHER_SCATTER(SDValue Op,
|
|
VECustomDAG &CDAG) const {
|
|
EVT DataVT = *getIdiomaticVectorType(Op.getNode());
|
|
auto Packing = getTypePacking(DataVT);
|
|
MVT LegalDataVT =
|
|
getLegalVectorType(Packing, DataVT.getVectorElementType().getSimpleVT());
|
|
|
|
SDValue AVL = getAnnotatedNodeAVL(Op).first;
|
|
SDValue Index = getGatherScatterIndex(Op);
|
|
SDValue BasePtr = getMemoryPtr(Op);
|
|
SDValue Mask = getNodeMask(Op);
|
|
SDValue Chain = getNodeChain(Op);
|
|
SDValue Scale = getGatherScatterScale(Op);
|
|
SDValue PassThru = getNodePassthru(Op);
|
|
SDValue StoredValue = getStoredValue(Op);
|
|
if (PassThru && PassThru->isUndef())
|
|
PassThru = SDValue();
|
|
|
|
bool IsScatter = (bool)StoredValue;
|
|
|
|
// TODO: Infer lower AVL from mask.
|
|
if (!AVL)
|
|
AVL = CDAG.getConstant(DataVT.getVectorNumElements(), MVT::i32);
|
|
|
|
// Default to the all-true mask.
|
|
if (!Mask)
|
|
Mask = CDAG.getConstantMask(Packing, true);
|
|
|
|
SDValue AddressVec =
|
|
CDAG.getGatherScatterAddress(BasePtr, Scale, Index, Mask, AVL);
|
|
if (IsScatter)
|
|
return CDAG.getNode(VEISD::VVP_SCATTER, MVT::Other,
|
|
{Chain, StoredValue, AddressVec, Mask, AVL});
|
|
|
|
// Gather.
|
|
SDValue NewLoadV = CDAG.getNode(VEISD::VVP_GATHER, {LegalDataVT, MVT::Other},
|
|
{Chain, AddressVec, Mask, AVL});
|
|
|
|
if (!PassThru)
|
|
return NewLoadV;
|
|
|
|
// TODO: Use vvp_select
|
|
SDValue DataV = CDAG.getNode(VEISD::VVP_SELECT, LegalDataVT,
|
|
{NewLoadV, PassThru, Mask, AVL});
|
|
SDValue NewLoadChainV = SDValue(NewLoadV.getNode(), 1);
|
|
return CDAG.getMergeValues({DataV, NewLoadChainV});
|
|
}
|
|
|
|
SDValue VETargetLowering::legalizeInternalLoadStoreOp(SDValue Op,
|
|
VECustomDAG &CDAG) const {
|
|
LLVM_DEBUG(dbgs() << "::legalizeInternalLoadStoreOp\n";);
|
|
MVT DataVT = getIdiomaticVectorType(Op.getNode())->getSimpleVT();
|
|
|
|
// TODO: Recognize packable load,store.
|
|
if (isPackedVectorType(DataVT))
|
|
return splitPackedLoadStore(Op, CDAG);
|
|
|
|
return legalizePackedAVL(Op, CDAG);
|
|
}
|
|
|
|
SDValue VETargetLowering::legalizeInternalVectorOp(SDValue Op,
|
|
SelectionDAG &DAG) const {
|
|
LLVM_DEBUG(dbgs() << "::legalizeInternalVectorOp\n";);
|
|
VECustomDAG CDAG(DAG, Op);
|
|
|
|
// Dispatch to specialized legalization functions.
|
|
switch (Op->getOpcode()) {
|
|
case VEISD::VVP_LOAD:
|
|
case VEISD::VVP_STORE:
|
|
return legalizeInternalLoadStoreOp(Op, CDAG);
|
|
}
|
|
|
|
EVT IdiomVT = Op.getValueType();
|
|
if (isPackedVectorType(IdiomVT) &&
|
|
!supportsPackedMode(Op.getOpcode(), IdiomVT))
|
|
return splitVectorOp(Op, CDAG);
|
|
|
|
// TODO: Implement odd/even splitting.
|
|
return legalizePackedAVL(Op, CDAG);
|
|
}
|
|
|
|
SDValue VETargetLowering::splitVectorOp(SDValue Op, VECustomDAG &CDAG) const {
|
|
MVT ResVT = splitVectorType(Op.getValue(0).getSimpleValueType());
|
|
|
|
auto AVLPos = getAVLPos(Op->getOpcode());
|
|
auto MaskPos = getMaskPos(Op->getOpcode());
|
|
|
|
SDValue PackedMask = getNodeMask(Op);
|
|
auto AVLPair = getAnnotatedNodeAVL(Op);
|
|
SDValue PackedAVL = AVLPair.first;
|
|
assert(!AVLPair.second && "Expecting non pack-legalized oepration");
|
|
|
|
// request the parts
|
|
SDValue PartOps[2];
|
|
|
|
SDValue UpperPartAVL; // we will use this for packing things back together
|
|
for (PackElem Part : {PackElem::Hi, PackElem::Lo}) {
|
|
// VP ops already have an explicit mask and AVL. When expanding from non-VP
|
|
// attach those additional inputs here.
|
|
auto SplitTM = CDAG.getTargetSplitMask(PackedMask, PackedAVL, Part);
|
|
|
|
if (Part == PackElem::Hi)
|
|
UpperPartAVL = SplitTM.AVL;
|
|
|
|
// Attach non-predicating value operands
|
|
SmallVector<SDValue, 4> OpVec;
|
|
for (unsigned i = 0; i < Op.getNumOperands(); ++i) {
|
|
if (AVLPos && ((int)i) == *AVLPos)
|
|
continue;
|
|
if (MaskPos && ((int)i) == *MaskPos)
|
|
continue;
|
|
|
|
// Value operand
|
|
auto PackedOperand = Op.getOperand(i);
|
|
auto UnpackedOpVT = splitVectorType(PackedOperand.getSimpleValueType());
|
|
SDValue PartV =
|
|
CDAG.getUnpack(UnpackedOpVT, PackedOperand, Part, SplitTM.AVL);
|
|
OpVec.push_back(PartV);
|
|
}
|
|
|
|
// Add predicating args and generate part node.
|
|
OpVec.push_back(SplitTM.Mask);
|
|
OpVec.push_back(SplitTM.AVL);
|
|
// Emit legal VVP nodes.
|
|
PartOps[(int)Part] =
|
|
CDAG.getNode(Op.getOpcode(), ResVT, OpVec, Op->getFlags());
|
|
}
|
|
|
|
// Re-package vectors.
|
|
return CDAG.getPack(Op.getValueType(), PartOps[(int)PackElem::Lo],
|
|
PartOps[(int)PackElem::Hi], UpperPartAVL);
|
|
}
|
|
|
|
SDValue VETargetLowering::legalizePackedAVL(SDValue Op,
|
|
VECustomDAG &CDAG) const {
|
|
LLVM_DEBUG(dbgs() << "::legalizePackedAVL\n";);
|
|
// Only required for VEC and VVP ops.
|
|
if (!isVVPOrVEC(Op->getOpcode()))
|
|
return Op;
|
|
|
|
// Operation already has a legal AVL.
|
|
auto AVL = getNodeAVL(Op);
|
|
if (isLegalAVL(AVL))
|
|
return Op;
|
|
|
|
// Half and round up EVL for 32bit element types.
|
|
SDValue LegalAVL = AVL;
|
|
MVT IdiomVT = getIdiomaticVectorType(Op.getNode())->getSimpleVT();
|
|
if (isPackedVectorType(IdiomVT)) {
|
|
assert(maySafelyIgnoreMask(Op) &&
|
|
"TODO Shift predication from EVL into Mask");
|
|
|
|
if (auto *ConstAVL = dyn_cast<ConstantSDNode>(AVL)) {
|
|
LegalAVL = CDAG.getConstant((ConstAVL->getZExtValue() + 1) / 2, MVT::i32);
|
|
} else {
|
|
auto ConstOne = CDAG.getConstant(1, MVT::i32);
|
|
auto PlusOne = CDAG.getNode(ISD::ADD, MVT::i32, {AVL, ConstOne});
|
|
LegalAVL = CDAG.getNode(ISD::SRL, MVT::i32, {PlusOne, ConstOne});
|
|
}
|
|
}
|
|
|
|
SDValue AnnotatedLegalAVL = CDAG.annotateLegalAVL(LegalAVL);
|
|
|
|
// Copy the operand list.
|
|
int NumOp = Op->getNumOperands();
|
|
auto AVLPos = getAVLPos(Op->getOpcode());
|
|
std::vector<SDValue> FixedOperands;
|
|
for (int i = 0; i < NumOp; ++i) {
|
|
if (AVLPos && (i == *AVLPos)) {
|
|
FixedOperands.push_back(AnnotatedLegalAVL);
|
|
continue;
|
|
}
|
|
FixedOperands.push_back(Op->getOperand(i));
|
|
}
|
|
|
|
// Clone the operation with fixed operands.
|
|
auto Flags = Op->getFlags();
|
|
SDValue NewN =
|
|
CDAG.getNode(Op->getOpcode(), Op->getVTList(), FixedOperands, Flags);
|
|
return NewN;
|
|
}
|