Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 39 additions & 2 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6963,7 +6963,7 @@ static bool hasPassthruOp(unsigned Opcode) {
Opcode <= RISCVISD::LAST_STRICTFP_OPCODE &&
"not a RISC-V target specific op");
static_assert(
RISCVISD::LAST_VL_VECTOR_OP - RISCVISD::FIRST_VL_VECTOR_OP == 133 &&
RISCVISD::LAST_VL_VECTOR_OP - RISCVISD::FIRST_VL_VECTOR_OP == 134 &&
RISCVISD::LAST_STRICTFP_OPCODE - RISCVISD::FIRST_STRICTFP_OPCODE == 21 &&
"adding target specific op should update this function");
if (Opcode >= RISCVISD::ADD_VL && Opcode <= RISCVISD::VFMAX_VL)
Expand All @@ -6987,7 +6987,7 @@ static bool hasMaskOp(unsigned Opcode) {
Opcode <= RISCVISD::LAST_STRICTFP_OPCODE &&
"not a RISC-V target specific op");
static_assert(
RISCVISD::LAST_VL_VECTOR_OP - RISCVISD::FIRST_VL_VECTOR_OP == 133 &&
RISCVISD::LAST_VL_VECTOR_OP - RISCVISD::FIRST_VL_VECTOR_OP == 134 &&
RISCVISD::LAST_STRICTFP_OPCODE - RISCVISD::FIRST_STRICTFP_OPCODE == 21 &&
"adding target specific op should update this function");
if (Opcode >= RISCVISD::TRUNCATE_VECTOR_VL && Opcode <= RISCVISD::SETCC_VL)
Expand Down Expand Up @@ -9595,6 +9595,13 @@ getSmallestVTForIndex(MVT VecVT, unsigned MaxIdx, SDLoc DL, SelectionDAG &DAG,
return SmallerVT;
}

static bool isValidVisniInsertExtractIndex(SDValue Idx) {
auto *IdxC = dyn_cast<ConstantSDNode>(Idx);
if (!IdxC || isNullConstant(Idx))
return false;
return isUInt<5>(IdxC->getZExtValue());
}

// Custom-legalize INSERT_VECTOR_ELT so that the value is inserted into the
// first position of a vector, and that vector is slid up to the insert index.
// By limiting the active vector length to index+1 and merging with the
Expand Down Expand Up @@ -9705,6 +9712,26 @@ SDValue RISCVTargetLowering::lowerINSERT_VECTOR_ELT(SDValue Op,
return Vec;
return convertFromScalableVector(VecVT, Vec, DAG, Subtarget);
}

// Use ri.vinsert.v.x if available.
if (Subtarget.hasVendorXRivosVisni() && VecVT.isInteger() &&
isValidVisniInsertExtractIndex(Idx)) {
unsigned Policy = RISCVVType::TAIL_UNDISTURBED_MASK_UNDISTURBED;
if (VecVT.isFixedLengthVector() && isa<ConstantSDNode>(Idx) &&
Idx->getAsZExtVal() + 1 == VecVT.getVectorNumElements())
Policy = RISCVVType::TAIL_AGNOSTIC;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm aware that this is copied from the existing case below, but if the VL here is always going to be >= VLMAX then can we not always set RISCVVType::TAIL_AGNOSTIC?

Copy link
Collaborator Author

@preames preames Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VL is always going to be <= VLMAX, not the other way around. But yes, you're correct that we can set TA in all cases here. I'll do that before landing.

Edit: For clarity, this depends on a specification clarification of what elements are tail elements for this instruction which hasn't yet been pushed. In short, it's the elements past VLMAX (since we allow writes past VL), but the usual past VL. This means that only fractional LMUL have tail elements.

SDValue PolicyOp =
DAG.getTargetConstant(Policy, DL, Subtarget.getXLenVT());
Vec = DAG.getNode(RISCVISD::RI_VINSERT_VL, DL, ContainerVT, Vec, Val, Idx,
VL, PolicyOp);
if (AlignedIdx)
Vec = DAG.getNode(ISD::INSERT_SUBVECTOR, DL, OrigContainerVT, OrigVec,
Vec, AlignedIdx);
if (!VecVT.isFixedLengthVector())
return Vec;
return convertFromScalableVector(VecVT, Vec, DAG, Subtarget);
}

ValInVec = lowerScalarInsert(Val, VL, ContainerVT, DL, DAG, Subtarget);
} else {
// On RV32, i64-element vectors must be specially handled to place the
Expand Down Expand Up @@ -9904,6 +9931,14 @@ SDValue RISCVTargetLowering::lowerEXTRACT_VECTOR_ELT(SDValue Op,
}
}

// Use ri.vextract.x.v if available.
// TODO: Avoid index 0 and just use the vmv.x.s
if (Subtarget.hasVendorXRivosVisni() && EltVT.isInteger() &&
isValidVisniInsertExtractIndex(Idx)) {
SDValue Elt = DAG.getNode(RISCVISD::RI_VEXTRACT, DL, XLenVT, Vec, Idx);
return DAG.getNode(ISD::TRUNCATE, DL, EltVT, Elt);
}

// If after narrowing, the required slide is still greater than LMUL2,
// fallback to generic expansion and go through the stack. This is done
// for a subtle reason: extracting *all* elements out of a vector is
Expand Down Expand Up @@ -22321,12 +22356,14 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
NODE_NAME_CASE(VZEXT_VL)
NODE_NAME_CASE(VCPOP_VL)
NODE_NAME_CASE(VFIRST_VL)
NODE_NAME_CASE(RI_VINSERT_VL)
NODE_NAME_CASE(RI_VZIPEVEN_VL)
NODE_NAME_CASE(RI_VZIPODD_VL)
NODE_NAME_CASE(RI_VZIP2A_VL)
NODE_NAME_CASE(RI_VZIP2B_VL)
NODE_NAME_CASE(RI_VUNZIP2A_VL)
NODE_NAME_CASE(RI_VUNZIP2B_VL)
NODE_NAME_CASE(RI_VEXTRACT)
NODE_NAME_CASE(READ_CSR)
NODE_NAME_CASE(WRITE_CSR)
NODE_NAME_CASE(SWAP_CSR)
Expand Down
10 changes: 10 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ enum NodeType : unsigned {
// vfirst.m with additional mask and VL operands.
VFIRST_VL,

// XRivosVisni
// VINSERT matches the semantics of ri.vinsert.v.x. It carries a VL operand.
RI_VINSERT_VL,

// XRivosVizip
RI_VZIPEVEN_VL,
RI_VZIPODD_VL,
Expand All @@ -414,6 +418,12 @@ enum NodeType : unsigned {

LAST_VL_VECTOR_OP = RI_VUNZIP2B_VL,

// XRivosVisni
// VEXTRACT matches the semantics of ri.vextract.x.v. The result is always
// XLenVT sign extended from the vector element size. VEXTRACT does *not*
// have a VL operand.
RI_VEXTRACT,

// Read VLENB CSR
READ_VLENB,
// Reads value of CSR.
Expand Down
12 changes: 11 additions & 1 deletion llvm/lib/Target/RISCV/RISCVInsertVSETVLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ static bool isFloatScalarMoveOrScalarSplatInstr(const MachineInstr &MI) {
}
}

static bool isVExtractInstr(const MachineInstr &MI) {
return RISCV::getRVVMCOpcode(MI.getOpcode()) == RISCV::RI_VEXTRACT;
}

static bool isScalarExtractInstr(const MachineInstr &MI) {
switch (RISCV::getRVVMCOpcode(MI.getOpcode())) {
default:
Expand Down Expand Up @@ -538,6 +542,12 @@ DemandedFields getDemanded(const MachineInstr &MI, const RISCVSubtarget *ST) {
Res.MaskPolicy = false;
}

if (isVExtractInstr(MI)) {
assert(!RISCVII::hasVLOp(TSFlags));
// TODO: LMUL can be any larger value (without cost)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be an interesting optimisation. I could imagine us having more demanded LMUL types e.g. Res.LMUL = Res.LMULGreaterThanOrEqualToMX

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a sense, we sort of already have this for vmv.s.x and vmv.x.s, it's just that the MX value is the smallest legal LMUL, and thus GTE becomes any LMUL.

Res.TailPolicy = false;
}

return Res;
}

Expand Down Expand Up @@ -1085,7 +1095,7 @@ RISCVInsertVSETVLI::computeInfoForInstr(const MachineInstr &MI) const {
InstrInfo.setAVLRegDef(VNI, VLOp.getReg());
}
} else {
assert(isScalarExtractInstr(MI));
assert(isScalarExtractInstr(MI) || isVExtractInstr(MI));
// Pick a random value for state tracking purposes, will be ignored via
// the demanded fields mechanism
InstrInfo.setAVLImm(1);
Expand Down
54 changes: 54 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoXRivos.td
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,57 @@ def RI_VEXTRACT : CustomRivosXVI<0b010111, OPMVV, (outs GPR:$rd),
(ins VR:$vs2, uimm5:$imm),
"ri.vextract.x.v", "$rd, $vs2, $imm">;
}


def ri_vextract : SDNode<"RISCVISD::RI_VEXTRACT",
SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisVec<1>,
SDTCisInt<2>,
SDTCisInt<1>]>>;

def ri_vinsert_vl : SDNode<"RISCVISD::RI_VINSERT_VL",
SDTypeProfile<1, 5, [SDTCisSameAs<0, 1>,
SDTCisInt<0>,
SDTCisVT<2, XLenVT>,
SDTCisVT<3, XLenVT>,
SDTCisVT<4, XLenVT>]>>;

let Predicates = [HasVendorXRivosVisni], mayLoad = 0, mayStore = 0,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not thrilled with this bit of tablegen, if anyone has suggestions on how to improve, please let me know!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks fine to me, is there anything in particular that you're worried about?

hasSideEffects = 0, HasSEWOp = 1 in
foreach m = MxList in {
defvar mx = m.MX;
let VLMul = m.value in {
let BaseInstr = RI_VEXTRACT in
def PseudoRI_VEXTRACT_ # mx :
Pseudo<(outs GPR:$rd), (ins m.vrclass:$rs2, uimm5:$idx, ixlenimm:$sew),
[]>,
RISCVVPseudo;

let HasVLOp = 1, BaseInstr = RI_VINSERT, HasVecPolicyOp = 1,
Constraints = "$rd = $rs1" in
def PseudoRI_VINSERT_ # mx :
Pseudo<(outs m.vrclass:$rd),
(ins m.vrclass:$rs1, GPR:$rs2, uimm5:$idx, AVL:$vl,
ixlenimm:$sew, ixlenimm:$policy),
[]>,
RISCVVPseudo;
}
}



foreach vti = AllIntegerVectors in
let Predicates = GetVTypePredicates<vti>.Predicates in {
def : Pat<(XLenVT (ri_vextract (vti.Vector vti.RegClass:$vs2), uimm5:$imm)),
(!cast<Instruction>("PseudoRI_VEXTRACT_" # vti.LMul.MX)
$vs2, uimm5:$imm, vti.Log2SEW)>;

def : Pat<(vti.Vector (ri_vinsert_vl (vti.Vector vti.RegClass:$merge),
vti.ScalarRegClass:$rs1,
uimm5:$imm,
VLOpFrag,
(XLenVT timm:$policy))),
(!cast<Instruction>("PseudoRI_VINSERT_" # vti.LMul.MX)
$merge, vti.ScalarRegClass:$rs1, uimm5:$imm,
GPR:$vl, vti.Log2SEW, (XLenVT timm:$policy))>;

}
Loading
Loading