Skip to content

Commit

Permalink
[LLVM][XTHeadVector] Implement intrinsics for vadd/vsub/vrsub. (llvm#48)
Browse files Browse the repository at this point in the history
* [LLVM][XTHeadVector] Define intrinsic
function for vadd, vsub and vrsub.

* [LLVM][XTHeadVector] Define pats and
pseudos for vadd, vrsub and vsub.

* [LLVM][XTHeadVector] Use th.vlse
when the xtheadvector extension is enabled.

* [LLVM][XTHeadVector] Match vrsub with
2 vector operands to th.vsub.vv

* [LLVM][XTHeadVector] Match th.vsub with
a small immediate to th.vadd.vi

* [LLVM][XTHeadVector] Add test cases.

* [NFC] Update README.
  • Loading branch information
AinsleySnow committed Jan 5, 2024
1 parent 06e6780 commit ca532a5
Show file tree
Hide file tree
Showing 7 changed files with 8,937 additions and 19 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ Any feature not listed below but present in the specification should be consider
- (Done) `7.5. Vector Load/Store Segment Operations (Zvlsseg)`, which is an essential part of `XTHeadVector`
- (Done) `8. Vector AMO Operations (Zvamo)`, which is `XTHeadZvamo` [regarding the `XTHeadVector` extension](https://github.com/T-head-Semi/thead-extension-spec/blob/24349e6df223e8b268ba9672297018f508670acb/xtheadvector.adoc?plain=1#L27).
- (WIP) `12. Vector Integer Arithmetic Operations`
- (WIP) `12.1. Vector Single-Width Integer Add and Subtract`
- (Done) `12.1. Vector Single-Width Integer Add and Subtract`
- (Done) `vadd.{vv,vx,vi}`
- (Done) `vsub.{vv,vx}`
- (Done) `vrsub.{vx,vi}`
- (WIP) Clang intrinsics related to the `XTHeadVector` extension:
- (WIP) `6. Configuration-Setting and Utility`
- (Done) `6.1. Set vl and vtype`
Expand Down
24 changes: 23 additions & 1 deletion llvm/include/llvm/IR/IntrinsicsRISCVXTHeadV.td
Original file line number Diff line number Diff line change
Expand Up @@ -561,9 +561,31 @@ let TargetPrefix = "riscv" in {
defm th_vamomaxu : XIntrinsicVAMO;
} // TargetPrefix = "riscv"

let TargetPrefix = "riscv" in {
// For destination vector type is the same as
// first source vector (with mask but no policy).
// Input: (maskedoff, vector_in, vector_in/scalar_in, mask, vl)
class XVBinaryAAXMasked
: DefaultAttrsIntrinsic<[llvm_anyvector_ty],
[LLVMMatchType<0>, LLVMMatchType<0>, llvm_any_ty,
LLVMScalarOrSameVectorWidth<0, llvm_i1_ty>, llvm_anyint_ty],
[IntrNoMem]>, RISCVVIntrinsic {
let ScalarOperand = 2;
let VLOperand = 4;
}

multiclass XVBinaryAAX {
def "int_riscv_" # NAME : RISCVBinaryAAXUnMasked;
def "int_riscv_" # NAME # "_mask" : XVBinaryAAXMasked;
}
}

let TargetPrefix = "riscv" in {
// 12. Vector Integer Arithmetic Instructions
defm th_vadd : RISCVBinaryAAX;
// 12.1. Vector Single-Width Integer Add and Subtract
defm th_vadd : XVBinaryAAX;
defm th_vsub : XVBinaryAAX;
defm th_vrsub : XVBinaryAAX;
} // TargetPrefix = "riscv"

let TargetPrefix = "riscv" in {
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ void RISCVDAGToDAGISel::PreprocessISelDAG() {
Chain = CurDAG->getNode(ISD::TokenFactor, DL, MVT::Other, Lo, Hi);

SDVTList VTs = CurDAG->getVTList({VT, MVT::Other});
SDValue IntID =
SDValue IntID = Subtarget->hasVendorXTHeadV() ?
CurDAG->getTargetConstant(Intrinsic::riscv_th_vlse, DL, MVT::i64) :
CurDAG->getTargetConstant(Intrinsic::riscv_vlse, DL, MVT::i64);
SDValue Ops[] = {Chain,
IntID,
Expand Down
249 changes: 242 additions & 7 deletions llvm/lib/Target/RISCV/RISCVInstrInfoXTHeadVPseudos.td
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,55 @@ let Predicates = [HasVendorXTHeadV, HasVendorXTHeadZvamo, HasStdExtA] in {
//===----------------------------------------------------------------------===//
// 12. Vector Integer Arithmetic Instructions
//===----------------------------------------------------------------------===//

//===----------------------------------------------------------------------===//
// Pseudos for arithmetic instructions.
//===----------------------------------------------------------------------===//

class XVPseudoBinaryNoMask<VReg RetClass,
VReg Op1Class,
DAGOperand Op2Class,
string Constraint> :
Pseudo<(outs RetClass:$rd),
(ins RetClass:$merge, Op1Class:$rs2, Op2Class:$rs1,
AVL:$vl, ixlenimm:$sew), []>,
RISCVVPseudo {
let mayLoad = 0;
let mayStore = 0;
let hasSideEffects = 0;
let Constraints = !interleave([Constraint, "$rd = $merge"], ",");
let HasVLOp = 1;
let HasSEWOp = 1;
}

multiclass XVPseudoBinary<VReg RetClass,
VReg Op1Class,
DAGOperand Op2Class,
LMULInfo MInfo,
string Constraint = "",
int sew = 0> {
let VLMul = MInfo.value, SEW=sew in {
defvar suffix = !if(sew, "_" # MInfo.MX # "_E" # sew, "_" # MInfo.MX);
def suffix : XVPseudoBinaryNoMask<RetClass, Op1Class, Op2Class,
Constraint>;
def suffix # "_MASK" : VPseudoBinaryMask<RetClass, Op1Class, Op2Class,
Constraint>,
RISCVMaskedPseudo<MaskIdx=3>;
}
}

multiclass XVPseudoBinaryV_VV<LMULInfo m, string Constraint = "", int sew = 0> {
defm _VV : XVPseudoBinary<m.vrclass, m.vrclass, m.vrclass, m, Constraint, sew>;
}

multiclass XVPseudoBinaryV_VX<LMULInfo m, string Constraint = "", int sew = 0> {
defm _VX : XVPseudoBinary<m.vrclass, m.vrclass, GPR, m, Constraint, sew>;
}

multiclass XVPseudoBinaryV_VI<Operand ImmType = simm5, LMULInfo m, string Constraint = ""> {
defm _VI : XVPseudoBinary<m.vrclass, m.vrclass, ImmType, m, Constraint>;
}

multiclass XVPseudoVALU_VV_VX_VI<Operand ImmType = simm5, string Constraint = ""> {
foreach m = MxListXTHeadV in {
defvar mx = m.MX;
Expand All @@ -1520,21 +1569,207 @@ multiclass XVPseudoVALU_VV_VX_VI<Operand ImmType = simm5, string Constraint = ""
defvar ReadVIALUV_MX = !cast<SchedRead>("ReadVIALUV_" # mx);
defvar ReadVIALUX_MX = !cast<SchedRead>("ReadVIALUX_" # mx);

defm "" : VPseudoBinaryV_VV<m, Constraint>,
Sched<[WriteVIALUV_MX, ReadVIALUV_MX, ReadVIALUV_MX, ReadVMask]>;
defm "" : VPseudoBinaryV_VX<m, Constraint>,
Sched<[WriteVIALUX_MX, ReadVIALUV_MX, ReadVIALUX_MX, ReadVMask]>;
defm "" : VPseudoBinaryV_VI<ImmType, m, Constraint>,
Sched<[WriteVIALUI_MX, ReadVIALUV_MX, ReadVMask]>;
defm "" : XVPseudoBinaryV_VV<m, Constraint>,
Sched<[WriteVIALUV_MX, ReadVIALUV_MX, ReadVIALUV_MX, ReadVMask]>;
defm "" : XVPseudoBinaryV_VX<m, Constraint>,
Sched<[WriteVIALUX_MX, ReadVIALUV_MX, ReadVIALUX_MX, ReadVMask]>;
defm "" : XVPseudoBinaryV_VI<ImmType, m, Constraint>,
Sched<[WriteVIALUI_MX, ReadVIALUV_MX, ReadVMask]>;
}
}

multiclass XVPseudoVALU_VV_VX {
foreach m = MxListXTHeadV in {
defvar mx = m.MX;
defvar WriteVIALUV_MX = !cast<SchedWrite>("WriteVIALUV_" # mx);
defvar WriteVIALUX_MX = !cast<SchedWrite>("WriteVIALUV_" # mx);
defvar ReadVIALUV_MX = !cast<SchedRead>("ReadVIALUV_" # mx);
defvar ReadVIALUX_MX = !cast<SchedRead>("ReadVIALUX_" # mx);

defm "" : XVPseudoBinaryV_VV<m>,
Sched<[WriteVIALUV_MX, ReadVIALUV_MX, ReadVIALUV_MX, ReadVMask]>;
defm "" : XVPseudoBinaryV_VX<m>,
Sched<[WriteVIALUX_MX, ReadVIALUV_MX, ReadVIALUX_MX, ReadVMask]>;
}
}

multiclass XVPseudoVALU_VX_VI<Operand ImmType = simm5> {
foreach m = MxListXTHeadV in {
defvar mx = m.MX;
defvar WriteVIALUX_MX = !cast<SchedWrite>("WriteVIALUX_" # mx);
defvar WriteVIALUI_MX = !cast<SchedWrite>("WriteVIALUI_" # mx);
defvar ReadVIALUV_MX = !cast<SchedRead>("ReadVIALUV_" # mx);
defvar ReadVIALUX_MX = !cast<SchedRead>("ReadVIALUX_" # mx);

defm "" : XVPseudoBinaryV_VX<m>,
Sched<[WriteVIALUX_MX, ReadVIALUV_MX, ReadVIALUX_MX, ReadVMask]>;
defm "" : XVPseudoBinaryV_VI<ImmType, m>,
Sched<[WriteVIALUI_MX, ReadVIALUV_MX, ReadVMask]>;
}
}

//===----------------------------------------------------------------------===//
// Helpers to define the intrinsic patterns for the XTHeadVector extension.
//===----------------------------------------------------------------------===//

class XVPatBinaryNoMask<string intrinsic_name,
string inst,
ValueType result_type,
ValueType op1_type,
ValueType op2_type,
int sew,
VReg result_reg_class,
VReg op1_reg_class,
DAGOperand op2_kind> :
Pat<(result_type (!cast<Intrinsic>(intrinsic_name)
(result_type result_reg_class:$merge),
(op1_type op1_reg_class:$rs1),
(op2_type op2_kind:$rs2),
VLOpFrag)),
(!cast<Instruction>(inst)
(result_type result_reg_class:$merge),
(op1_type op1_reg_class:$rs1),
(op2_type op2_kind:$rs2),
GPR:$vl, sew)>;

multiclass XVPatBinary<string intrinsic,
string inst,
ValueType result_type,
ValueType op1_type,
ValueType op2_type,
ValueType mask_type,
int sew,
VReg result_reg_class,
VReg op1_reg_class,
DAGOperand op2_kind> {
def : XVPatBinaryNoMask<intrinsic, inst, result_type, op1_type, op2_type,
sew, result_reg_class, op1_reg_class, op2_kind>;
def : VPatBinaryMask<intrinsic, inst, result_type, op1_type, op2_type,
mask_type, sew, result_reg_class, op1_reg_class,
op2_kind>;
}

multiclass XVPatBinaryV_VV<string intrinsic, string instruction,
list<VTypeInfo> vtilist, bit isSEWAware = 0> {
foreach vti = vtilist in
let Predicates = GetXVTypePredicates<vti>.Predicates in
defm : XVPatBinary<intrinsic,
!if(isSEWAware,
instruction # "_VV_" # vti.LMul.MX # "_E" # vti.SEW,
instruction # "_VV_" # vti.LMul.MX),
vti.Vector, vti.Vector, vti.Vector,vti.Mask,
vti.Log2SEW, vti.RegClass,
vti.RegClass, vti.RegClass>;
}

multiclass XVPatBinaryV_VX<string intrinsic, string instruction,
list<VTypeInfo> vtilist, bit isSEWAware = 0> {
foreach vti = vtilist in {
defvar kind = "V"#vti.ScalarSuffix;
let Predicates = GetXVTypePredicates<vti>.Predicates in
defm : XVPatBinary<intrinsic,
!if(isSEWAware,
instruction#"_"#kind#"_"#vti.LMul.MX#"_E"#vti.SEW,
instruction#"_"#kind#"_"#vti.LMul.MX),
vti.Vector, vti.Vector, vti.Scalar, vti.Mask,
vti.Log2SEW, vti.RegClass,
vti.RegClass, vti.ScalarRegClass>;
}
}

multiclass XVPatBinaryV_VI<string intrinsic, string instruction,
list<VTypeInfo> vtilist, Operand imm_type> {
foreach vti = vtilist in
let Predicates = GetXVTypePredicates<vti>.Predicates in
defm : XVPatBinary<intrinsic, instruction # "_VI_" # vti.LMul.MX,
vti.Vector, vti.Vector, XLenVT, vti.Mask,
vti.Log2SEW, vti.RegClass,
vti.RegClass, imm_type>;
}

multiclass XVPatBinaryV_VV_VX_VI<string intrinsic, string instruction,
list<VTypeInfo> vtilist, Operand ImmType = simm5>
: XVPatBinaryV_VV<intrinsic, instruction, vtilist>,
XVPatBinaryV_VX<intrinsic, instruction, vtilist>,
XVPatBinaryV_VI<intrinsic, instruction, vtilist, ImmType>;

multiclass XVPatBinaryV_VV_VX<string intrinsic, string instruction,
list<VTypeInfo> vtilist, bit isSEWAware = 0>
: XVPatBinaryV_VV<intrinsic, instruction, vtilist, isSEWAware>,
XVPatBinaryV_VX<intrinsic, instruction, vtilist, isSEWAware>;

multiclass XVPatBinaryV_VX_VI<string intrinsic, string instruction,
list<VTypeInfo> vtilist>
: XVPatBinaryV_VX<intrinsic, instruction, vtilist>,
XVPatBinaryV_VI<intrinsic, instruction, vtilist, simm5>;

//===----------------------------------------------------------------------===//
// 12.1. Vector Single-Width Saturating Add and Subtract
//===----------------------------------------------------------------------===//
let Predicates = [HasVendorXTHeadV] in {
defm PseudoTH_VADD : XVPseudoVALU_VV_VX_VI;
defm PseudoTH_VSUB : XVPseudoVALU_VV_VX;
defm PseudoTH_VRSUB : XVPseudoVALU_VX_VI;

foreach vti = AllIntegerXVectors in {
// Match vrsub with 2 vector operands to th.vsub.vv by swapping operands. This
// Occurs when legalizing th.vrsub.vx intrinsics for i64 on RV32 since we need
// to use a more complex splat sequence. Add the pattern for all VTs for
// consistency.
let Predicates = GetXVTypePredicates<vti>.Predicates in {
def : Pat<(vti.Vector (int_riscv_th_vrsub (vti.Vector vti.RegClass:$merge),
(vti.Vector vti.RegClass:$rs2),
(vti.Vector vti.RegClass:$rs1),
VLOpFrag)),
(!cast<Instruction>("PseudoTH_VSUB_VV_"#vti.LMul.MX)
vti.RegClass:$merge,
vti.RegClass:$rs1,
vti.RegClass:$rs2,
GPR:$vl,
vti.Log2SEW)>;
def : Pat<(vti.Vector (int_riscv_th_vrsub_mask (vti.Vector vti.RegClass:$merge),
(vti.Vector vti.RegClass:$rs2),
(vti.Vector vti.RegClass:$rs1),
(vti.Mask V0),
VLOpFrag)),
(!cast<Instruction>("PseudoTH_VSUB_VV_"#vti.LMul.MX#"_MASK")
vti.RegClass:$merge,
vti.RegClass:$rs1,
vti.RegClass:$rs2,
(vti.Mask V0),
GPR:$vl,
vti.Log2SEW)>;

// Match th.vsub with a small immediate to th.vadd.vi by negating the immediate.
def : Pat<(vti.Vector (int_riscv_th_vsub (vti.Vector (undef)),
(vti.Vector vti.RegClass:$rs1),
(vti.Scalar simm5_plus1:$rs2),
VLOpFrag)),
(!cast<Instruction>("PseudoTH_VADD_VI_"#vti.LMul.MX) (vti.Vector (IMPLICIT_DEF)),
vti.RegClass:$rs1,
(NegImm simm5_plus1:$rs2),
GPR:$vl,
vti.Log2SEW)>;
def : Pat<(vti.Vector (int_riscv_th_vsub_mask (vti.Vector vti.RegClass:$merge),
(vti.Vector vti.RegClass:$rs1),
(vti.Scalar simm5_plus1:$rs2),
(vti.Mask V0),
VLOpFrag)),
(!cast<Instruction>("PseudoTH_VADD_VI_"#vti.LMul.MX#"_MASK")
vti.RegClass:$merge,
vti.RegClass:$rs1,
(NegImm simm5_plus1:$rs2),
(vti.Mask V0),
GPR:$vl,
vti.Log2SEW)>;
}
}
} // Predicates = [HasVendorXTHeadV]

let Predicates = [HasVendorXTHeadV] in {
defm : VPatBinaryV_VV_VX_VI<"int_riscv_th_vadd", "PseudoTH_VADD", AllIntegerXVectors>;
defm : XVPatBinaryV_VV_VX_VI<"int_riscv_th_vadd", "PseudoTH_VADD", AllIntegerXVectors>;
defm : XVPatBinaryV_VV_VX<"int_riscv_th_vsub", "PseudoTH_VSUB", AllIntegerXVectors>;
defm : XVPatBinaryV_VX_VI<"int_riscv_th_vrsub", "PseudoTH_VRSUB", AllIntegerXVectors>;
} // Predicates = [HasVendorXTHeadV]

//===----------------------------------------------------------------------===//
Expand Down
Loading

0 comments on commit ca532a5

Please sign in to comment.