Files
TheRock/compiler/amd-llvm/llvm/lib/Target/NVPTX/NVPTXInstrInfo.td
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

2612 lines
102 KiB
TableGen

//===- NVPTXInstrInfo.td - NVPTX Instruction defs -------------*- tblgen-*-===//
//
// 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 describes the PTX instructions in TableGen format.
//
//===----------------------------------------------------------------------===//
include "NVPTXInstrFormats.td"
let OperandType = "OPERAND_IMMEDIATE" in {
def f16imm : Operand<f16>;
def bf16imm : Operand<bf16>;
}
//===----------------------------------------------------------------------===//
// NVPTX Operand Definitions.
//===----------------------------------------------------------------------===//
def brtarget : Operand<OtherVT>;
// CVT conversion modes
// These must match the enum in NVPTX.h
def CvtNONE : PatLeaf<(i32 0x0)>;
def CvtRNI : PatLeaf<(i32 0x1)>;
def CvtRZI : PatLeaf<(i32 0x2)>;
def CvtRMI : PatLeaf<(i32 0x3)>;
def CvtRPI : PatLeaf<(i32 0x4)>;
def CvtRN : PatLeaf<(i32 0x5)>;
def CvtRZ : PatLeaf<(i32 0x6)>;
def CvtRM : PatLeaf<(i32 0x7)>;
def CvtRP : PatLeaf<(i32 0x8)>;
def CvtRNA : PatLeaf<(i32 0x9)>;
def CvtRS : PatLeaf<(i32 0xA)>;
def CvtNONE_FTZ : PatLeaf<(i32 0x10)>;
def CvtRNI_FTZ : PatLeaf<(i32 0x11)>;
def CvtRZI_FTZ : PatLeaf<(i32 0x12)>;
def CvtRMI_FTZ : PatLeaf<(i32 0x13)>;
def CvtRPI_FTZ : PatLeaf<(i32 0x14)>;
def CvtRN_FTZ : PatLeaf<(i32 0x15)>;
def CvtRZ_FTZ : PatLeaf<(i32 0x16)>;
def CvtRM_FTZ : PatLeaf<(i32 0x17)>;
def CvtRP_FTZ : PatLeaf<(i32 0x18)>;
def CvtSAT : PatLeaf<(i32 0x20)>;
def CvtSAT_FTZ : PatLeaf<(i32 0x30)>;
def CvtNONE_RELU : PatLeaf<(i32 0x40)>;
def CvtRN_RELU : PatLeaf<(i32 0x45)>;
def CvtRZ_RELU : PatLeaf<(i32 0x46)>;
def CvtRS_RELU : PatLeaf<(i32 0x4A)>;
def CvtNONE_SATFINITE : PatLeaf<(i32 0x80)>;
def CvtRN_SATFINITE : PatLeaf<(i32 0x85)>;
def CvtRN_RELU_SATFINITE : PatLeaf<(i32 0xC5)>;
def CvtMode : Operand<i32> {
let PrintMethod = "printCvtMode";
}
// FTZ flag
def FTZ : PatLeaf<(i1 1)>;
def NoFTZ : PatLeaf<(i1 0)>;
def getFTZFlag : SDNodeXForm<imm, [{
(void)N;
return CurDAG->getTargetConstant(useF32FTZ() ? 1 : 0, SDLoc(), MVT::i1);
}]>;
def FTZFlag : OperandWithDefaultOps<i1, (ops (getFTZFlag (i1 0)))> {
let PrintMethod = "printFTZFlag";
}
// Flag to indicate predicate negation (@!p) for conditional branches.
def BranchFlag : OperandWithDefaultOps<i1, (ops (i1 0))> {
let PrintMethod = "printNegatedPredicate";
}
// Compare modes
// These must match the enum in NVPTX.h
def CmpEQ : PatLeaf<(i32 0)>;
def CmpNE : PatLeaf<(i32 1)>;
def CmpMode : Operand<i32> {
let PrintMethod = "printCmpMode";
}
// PRMT modes
// These must match the enum in NVPTX.h
def PrmtNONE : PatLeaf<(i32 0x0)>;
def PrmtF4E : PatLeaf<(i32 0x1)>;
def PrmtB4E : PatLeaf<(i32 0x2)>;
def PrmtRC8 : PatLeaf<(i32 0x3)>;
def PrmtECL : PatLeaf<(i32 0x4)>;
def PrmtECR : PatLeaf<(i32 0x5)>;
def PrmtRC16 : PatLeaf<(i32 0x6)>;
def PrmtMode : Operand<i32> {
let PrintMethod = "printPrmtMode";
}
//===----------------------------------------------------------------------===//
// NVPTX Instruction Predicate Definitions
//===----------------------------------------------------------------------===//
// Checks PTX version and family-specific and architecture-specific SM versions.
// For example, sm_100{f/a} and any future variants in the same family will match
// for any PTX version greater than or equal to `PTXVersion`.
class PTXWithFamilySMs<int PTXVersion, list<int> SMVersions> :
Predicate<"Subtarget->hasPTXWithFamilySMs(" # PTXVersion # ", {" #
!interleave(SMVersions, ", ") # "})">;
// Checks PTX version and architecture-specific SM versions.
// For example, sm_100{a} will match for any PTX version
// greater than or equal to `PTXVersion`.
class PTXWithAccelSMs<int PTXVersion, list<int> SMVersions> :
Predicate<"Subtarget->hasPTXWithAccelSMs(" # PTXVersion # ", {" #
!interleave(SMVersions, ", ") # "})">;
// Helper predicate to call a subtarget method.
class callSubtarget<string SubtargetMethod> : Predicate<"Subtarget->" # SubtargetMethod # "()">;
def hasAtomAddF64 : Predicate<"Subtarget->hasAtomAddF64()">;
def hasAtomScope : Predicate<"Subtarget->hasAtomScope()">;
def hasAtomBitwise64 : Predicate<"Subtarget->hasAtomBitwise64()">;
def hasAtomMinMax64 : Predicate<"Subtarget->hasAtomMinMax64()">;
def hasAtomSwap128 : Predicate<"Subtarget->hasAtomSwap128()">;
def hasClusters : Predicate<"Subtarget->hasClusters()">;
def hasPTXASUnreachableBug : Predicate<"Subtarget->hasPTXASUnreachableBug()">;
def noPTXASUnreachableBug : Predicate<"!Subtarget->hasPTXASUnreachableBug()">;
def hasOptEnabled : Predicate<"TM.getOptLevel() != CodeGenOptLevel::None">;
def hasArchAccelFeatures : Predicate<"Subtarget->hasArchAccelFeatures()">;
def hasFamilySpecificFeatures : Predicate<"Subtarget->hasFamilySpecificFeatures()">;
def doF32FTZ : Predicate<"useF32FTZ()">;
def doNoF32FTZ : Predicate<"!useF32FTZ()">;
def doRsqrtOpt : Predicate<"doRsqrtOpt()">;
def doMADWideOpt : Predicate<"doMADWideOpt()">;
def hasHWROT32 : Predicate<"Subtarget->hasHWROT32()">;
def noHWROT32 : Predicate<"!Subtarget->hasHWROT32()">;
def hasDotInstructions : Predicate<"Subtarget->hasDotInstructions()">;
def hasF32x2Instructions : Predicate<"Subtarget->hasF32x2Instructions()">;
class hasPTX<int version>: Predicate<"Subtarget->getPTXVersion() >= " # version>;
class hasSM<int version>: Predicate<"Subtarget->getSmVersion() >= " # version>;
// Explicit records for arch-accelerated SM versions
def hasSM90a : Predicate<"Subtarget->getSmVersion() == 90 && Subtarget->hasArchAccelFeatures()">;
def hasSM100a : Predicate<"Subtarget->getSmVersion() == 100 && Subtarget->hasArchAccelFeatures()">;
def hasSM101a : Predicate<"Subtarget->getSmVersion() == 101 && Subtarget->hasArchAccelFeatures()">;
def hasSM120a : Predicate<"Subtarget->getSmVersion() == 120 && Subtarget->hasArchAccelFeatures()">;
def hasSM100aOrSM103a :
Predicate<"(Subtarget->getSmVersion() == 100 || " #
"Subtarget->getSmVersion() == 103) " #
"&& Subtarget->hasArchAccelFeatures()">;
// non-sync shfl instructions are not available on sm_70+ in PTX6.4+
def hasSHFL : Predicate<"!(Subtarget->getSmVersion() >= 70"
"&& Subtarget->getPTXVersion() >= 64)">;
def useFP16Math: Predicate<"Subtarget->allowFP16Math()">;
def hasBF16Math: Predicate<"Subtarget->hasBF16Math()">;
//===----------------------------------------------------------------------===//
// Some Common Instruction Class Templates
//===----------------------------------------------------------------------===//
class OneUse1<SDPatternOperator operator>
: PatFrag<(ops node:$A), (operator node:$A), [{ return N->hasOneUse(); }]>;
class OneUse2<SDPatternOperator operator>
: PatFrag<(ops node:$A, node:$B), (operator node:$A, node:$B), [{ return N->hasOneUse(); }]>;
class zeroinitializer<ValueType vt> :
PatLeaf<(vt (bitconvert (!cast<ValueType>("i" # vt.Size) 0)))>;
def fpimm_pos_inf : FPImmLeaf<fAny, [{ return Imm.isPosInfinity(); }]>;
def fpimm_0 : FPImmLeaf<fAny, [{ return Imm.isZero(); }]>;
def fpimm_1 : FPImmLeaf<fAny, [{ return Imm.isExactlyValue(1.0); }]>;
def fpimm_neg_1 : FPImmLeaf<fAny, [{ return Imm.isExactlyValue(-1.0); }]>;
// Operands which can hold a Register or an Immediate.
//
// Unfortunately, since most register classes can hold multiple types, we must
// use the 'Any' type for these.
def RI1 : Operand<i1>;
def RI16 : Operand<Any>;
def RI32 : Operand<Any>;
def RI64 : Operand<Any>;
// Utility class to wrap up information about a register and DAG type for more
// convenient iteration and parameterization
class RegTyInfo<ValueType ty, NVPTXRegClass rc, string ptx_type, Operand imm, SDNode imm_node,
bit supports_imm = 1> {
ValueType Ty = ty;
NVPTXRegClass RC = rc;
Operand Imm = imm;
SDNode ImmNode = imm_node;
bit SupportsImm = supports_imm;
int Size = ty.Size;
string PtxType = ptx_type;
}
def I1RT : RegTyInfo<i1, B1, "pred", i1imm, imm>;
def I16RT : RegTyInfo<i16, B16, "b16", i16imm, imm>;
def I32RT : RegTyInfo<i32, B32, "b32", i32imm, imm>;
def I64RT : RegTyInfo<i64, B64, "b64", i64imm, imm>;
def F32RT : RegTyInfo<f32, B32, "f32", f32imm, fpimm>;
def F64RT : RegTyInfo<f64, B64, "f64", f64imm, fpimm>;
def F16RT : RegTyInfo<f16, B16, "f16", f16imm, fpimm, supports_imm = 0>;
def BF16RT : RegTyInfo<bf16, B16, "bf16", bf16imm, fpimm, supports_imm = 0>;
def F16X2RT : RegTyInfo<v2f16, B32, "f16x2", ?, ?, supports_imm = 0>;
def BF16X2RT : RegTyInfo<v2bf16, B32, "bf16x2", ?, ?, supports_imm = 0>;
def F32X2RT : RegTyInfo<v2f32, B64, "f32x2", ?, ?, supports_imm = 0>;
// This class provides a basic wrapper around an NVPTXInst that abstracts the
// specific syntax of most PTX instructions. It automatically handles the
// construction of the asm string based on the provided dag arguments.
// For example, the following asm-strings would be computed:
//
// * BasicFlagsNVPTXInst<(outs B32:$dst),
// (ins B32:$a, B32:$b), (ins),
// "add.s32">;
// ---> "add.s32 \t$dst, $a, $b;"
//
// * BasicFlagsNVPTXInst<(outs B32:$d),
// (ins B32:$a, B32:$b, Hexu32imm:$c),
// (ins PrmtMode:$mode),
// "prmt.b32${mode}">;
// ---> "prmt.b32${mode} \t$d, $a, $b, $c;"
//
// * BasicFlagsNVPTXInst<(outs B64:$state),
// (ins ADDR:$addr),
// "mbarrier.arrive.b64">;
// ---> "mbarrier.arrive.b64 \t$state, [$addr];"
//
class BasicFlagsNVPTXInst<dag outs_dag, dag ins_dag, dag flags_dag, string asmstr,
list<dag> pattern = []>
: NVPTXInst<
outs_dag,
!con(ins_dag, flags_dag),
!strconcat(
asmstr,
!if(!and(!empty(ins_dag), !empty(outs_dag)), "",
!strconcat(
" \t",
!interleave(
!foreach(i, !range(!size(outs_dag)),
"$" # !getdagname(outs_dag, i)),
"|"),
!if(!or(!empty(ins_dag), !empty(outs_dag)), "", ", "),
!interleave(
!foreach(i, !range(!size(ins_dag)),
!if(!eq(!cast<string>(!getdagarg<DAGOperand>(ins_dag, i)), "ADDR"),
"[$" # !getdagname(ins_dag, i) # "]",
"$" # !getdagname(ins_dag, i)
)
),
", "))),
";"),
pattern>;
class BasicNVPTXInst<dag outs, dag insv, string asmstr, list<dag> pattern = []>
: BasicFlagsNVPTXInst<outs, insv, (ins), asmstr, pattern>;
multiclass I3Inst<string op_str, SDPatternOperator op_node, RegTyInfo t,
bit commutative, list<Predicate> requires = []> {
def rr :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, t.RC:$b),
op_str,
[(set t.Ty:$dst, (op_node t.Ty:$a, t.Ty:$b))]>,
Requires<requires>;
def ri :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, t.Imm:$b),
op_str,
[(set t.Ty:$dst, (op_node t.Ty:$a, (t.Ty imm:$b)))]>,
Requires<requires>;
if !not(commutative) then
def ir :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.Imm:$a, t.RC:$b),
op_str,
[(set t.Ty:$dst, (op_node (t.Ty imm:$a), t.Ty:$b))]>,
Requires<requires>;
}
// Template for instructions which take three int64, int32, or int16 args.
// The instructions are named "<OpcStr><Width>" (e.g. "add.s64").
multiclass I3<string op_str, SDPatternOperator op_node, bit commutative> {
foreach t = [I16RT, I32RT, I64RT] in
defm t.Size# : I3Inst<op_str # t.Size, op_node, t, commutative>;
}
class I16x2<string OpcStr, SDNode OpNode> :
BasicNVPTXInst<(outs B32:$dst), (ins B32:$a, B32:$b),
OpcStr # "16x2",
[(set v2i16:$dst, (OpNode v2i16:$a, v2i16:$b))]>,
Requires<[hasPTX<80>, hasSM<90>]>;
// Template for instructions which take 3 int args. The instructions are
// named "<OpcStr>.s32" (e.g. "addc.cc.s32").
multiclass ADD_SUB_INT_CARRY<string op_str, SDNode op_node, bit commutative> {
let hasSideEffects = 1 in {
defm i32 : I3Inst<op_str # ".s32", op_node, I32RT, commutative>;
defm i64 : I3Inst<op_str # ".s64", op_node, I64RT, commutative,
requires = [hasPTX<43>]>;
}
}
// Template for minimum/maximum instructions.
//
// Also defines ftz (flush subnormal inputs and results to sign-preserving
// zero) variants for fp32 functions.
multiclass FMINIMUMMAXIMUM<string OpcStr, bit NaN, SDPatternOperator OpNode> {
defvar nan_str = !if(NaN, ".NaN", "");
if !not(NaN) then {
def _f64_rr :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$a, B64:$b),
OpcStr # ".f64",
[(set f64:$dst, (OpNode f64:$a, f64:$b))]>;
def _f64_ri :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$a, f64imm:$b),
OpcStr # ".f64",
[(set f64:$dst, (OpNode f64:$a, fpimm:$b))]>;
}
def _f32_rr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f32",
[(set f32:$dst, (OpNode f32:$a, f32:$b))]>;
def _f32_ri :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, f32imm:$b),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f32",
[(set f32:$dst, (OpNode f32:$a, fpimm:$b))]>;
def _f16_rr :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B16:$a, B16:$b),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f16",
[(set f16:$dst, (OpNode f16:$a, f16:$b))]>,
Requires<[useFP16Math]>;
def _f16x2_rr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f16x2",
[(set v2f16:$dst, (OpNode v2f16:$a, v2f16:$b))]>,
Requires<[useFP16Math, hasSM<80>, hasPTX<70>]>;
def _bf16_rr :
BasicNVPTXInst<(outs B16:$dst),
(ins B16:$a, B16:$b),
OpcStr # nan_str # ".bf16",
[(set bf16:$dst, (OpNode bf16:$a, bf16:$b))]>,
Requires<[hasBF16Math, hasSM<80>, hasPTX<70>]>;
def _bf16x2_rr :
BasicNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b),
OpcStr # nan_str # ".bf16x2",
[(set v2bf16:$dst, (OpNode v2bf16:$a, v2bf16:$b))]>,
Requires<[hasBF16Math, hasSM<80>, hasPTX<70>]>;
}
// Template for 3-input minimum/maximum instructions
// (sm_100+/PTX 8.8 and f32 only)
//
// Also defines ftz (flush subnormal inputs and results to sign-preserving
// zero) variants for fp32 functions.
multiclass FMINIMUMMAXIMUM3<string OpcStr, bit NaN, SDNode OpNode> {
defvar nan_str = !if(NaN, ".NaN", "");
def f32rrr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b, B32:$c),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f32",
[(set f32:$dst, (OpNode f32:$a, f32:$b, f32:$c))]>,
Requires<[hasPTX<88>, hasSM<100>]>;
def f32rri :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b, f32imm:$c),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f32",
[(set f32:$dst, (OpNode f32:$a, f32:$b, fpimm:$c))]>,
Requires<[hasPTX<88>, hasSM<100>]>;
def f32rii :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, f32imm:$b, f32imm:$c),
(ins FTZFlag:$ftz),
OpcStr # "$ftz" # nan_str # ".f32",
[(set f32:$dst, (OpNode f32:$a, fpimm:$b, fpimm:$c))]>,
Requires<[hasPTX<88>, hasSM<100>]>;
}
// Template for instructions which take three FP args. The
// instructions are named "<OpcStr>.f<Width>" (e.g. "add.f64").
//
// Also defines ftz (flush subnormal inputs and results to sign-preserving
// zero) variants for fp32/fp16 functions.
//
// This multiclass should be used for nodes that can be folded to make fma ops.
// In this case, we use the ".rn" variant when FMA is disabled, as this behaves
// just like the non ".rn" op, but prevents ptxas from creating FMAs.
multiclass F3<string op_str, SDPatternOperator op_pat> {
def f64rr :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$a, B64:$b),
op_str # ".f64",
[(set f64:$dst, (op_pat f64:$a, f64:$b))]>;
def f64ri :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$a, f64imm:$b),
op_str # ".f64",
[(set f64:$dst, (op_pat f64:$a, fpimm:$b))]>;
def f32rr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b),
(ins FTZFlag:$ftz),
op_str # "$ftz.f32",
[(set f32:$dst, (op_pat f32:$a, f32:$b))]>;
def f32ri :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, f32imm:$b),
(ins FTZFlag:$ftz),
op_str # "$ftz.f32",
[(set f32:$dst, (op_pat f32:$a, fpimm:$b))]>;
def f16rr :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B16:$a, B16:$b),
(ins FTZFlag:$ftz),
op_str # "$ftz.f16",
[(set f16:$dst, (op_pat f16:$a, f16:$b))]>,
Requires<[useFP16Math]>;
def f32x2rr :
BasicFlagsNVPTXInst<(outs B64:$dst),
(ins B64:$a, B64:$b),
(ins FTZFlag:$ftz),
op_str # "$ftz.f32x2",
[(set v2f32:$dst, (op_pat v2f32:$a, v2f32:$b))]>,
Requires<[hasF32x2Instructions]>;
def f16x2rr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b),
(ins FTZFlag:$ftz),
op_str # "$ftz.f16x2",
[(set v2f16:$dst, (op_pat v2f16:$a, v2f16:$b))]>,
Requires<[useFP16Math]>;
def bf16rr :
BasicNVPTXInst<(outs B16:$dst),
(ins B16:$a, B16:$b),
op_str # ".bf16",
[(set bf16:$dst, (op_pat bf16:$a, bf16:$b))]>,
Requires<[hasBF16Math]>;
def bf16x2rr :
BasicNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b),
op_str # ".bf16x2",
[(set v2bf16:$dst, (op_pat v2bf16:$a, v2bf16:$b))]>,
Requires<[hasBF16Math]>;
}
class BinOpAllowsFMA<SDPatternOperator operator>
: PatFrag<(ops node:$A, node:$B),
(operator node:$A, node:$B), [{
return allowFMA() || N->getFlags().hasAllowContract();
}]>;
multiclass F3_fma_component<string op_str, SDNode op_node> {
defm "" : F3<op_str, BinOpAllowsFMA<op_node>>;
defm _rn : F3<op_str # ".rn", op_node>;
}
// Template for operations which take two f32 or f64 operands. Provides three
// instructions: <OpcStr>.f64, <OpcStr>.f32, and <OpcStr>.ftz.f32 (flush
// subnormal inputs and results to zero).
multiclass F2<string OpcStr, SDNode OpNode> {
def f64 : BasicNVPTXInst<(outs B64:$dst), (ins B64:$a),
OpcStr # ".f64",
[(set f64:$dst, (OpNode f64:$a))]>;
def f32 : BasicFlagsNVPTXInst<(outs B32:$dst), (ins B32:$a),
(ins FTZFlag:$ftz),
OpcStr # "$ftz.f32",
[(set f32:$dst, (OpNode f32:$a))]>;
}
multiclass F2_Support_Half<string OpcStr, SDNode OpNode> {
def bf16 : BasicNVPTXInst<(outs B16:$dst), (ins B16:$a),
OpcStr # ".bf16",
[(set bf16:$dst, (OpNode bf16:$a))]>,
Requires<[hasSM<80>, hasPTX<70>]>;
def bf16x2 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$a),
OpcStr # ".bf16x2",
[(set v2bf16:$dst, (OpNode v2bf16:$a))]>,
Requires<[hasSM<80>, hasPTX<70>]>;
def f16 : BasicFlagsNVPTXInst<(outs B16:$dst), (ins B16:$a),
(ins FTZFlag:$ftz),
OpcStr # "$ftz.f16",
[(set f16:$dst, (OpNode f16:$a))]>,
Requires<[hasSM<53>, hasPTX<65>]>;
def f16x2 : BasicFlagsNVPTXInst<(outs B32:$dst), (ins B32:$a),
(ins FTZFlag:$ftz),
OpcStr # "$ftz.f16x2",
[(set v2f16:$dst, (OpNode v2f16:$a))]>,
Requires<[hasSM<53>, hasPTX<65>]>;
}
//===----------------------------------------------------------------------===//
// NVPTX Instructions.
//===----------------------------------------------------------------------===//
//-----------------------------------
// Type Conversion
//-----------------------------------
let hasSideEffects = false in {
// Generate a cvt to the given type from all possible types. Each instance
// takes a CvtMode immediate that defines the conversion mode to use. It can
// be CvtNONE to omit a conversion mode.
multiclass CVT_FROM_ALL<string ToType, RegisterClass RC, list<Predicate> Preds = []> {
foreach sign = ["s", "u"] in {
def _ # sign # "8" :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B16:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:sat}." # ToType # "." # sign # "8">,
Requires<Preds>;
def _ # sign # "16" :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B16:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:sat}." # ToType # "." # sign # "16">,
Requires<Preds>;
def _ # sign # "32" :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:sat}." # ToType # "." # sign # "32">,
Requires<Preds>;
def _ # sign # "64" :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B64:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:sat}." # ToType # "." # sign # "64">,
Requires<Preds>;
}
def _f16 :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B16:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:sat}." # ToType # ".f16">,
Requires<Preds>;
def _bf16 :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B16:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:relu}${mode:sat}." # ToType # ".bf16">,
Requires<!if(!eq(ToType, "f32"),
// bf16->f32 was introduced early.
[hasPTX<71>, hasSM<80>],
// bf16->everything else needs sm90/ptx78
[hasPTX<78>, hasSM<90>])>;
def _f32 :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:relu}${mode:sat}." # ToType # ".f32">,
Requires<!if(!eq(ToType, "bf16"),
// f32->bf16 was introduced early.
[hasPTX<70>, hasSM<80>],
Preds)>;
def _f64 :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B64:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:ftz}${mode:sat}." # ToType # ".f64">,
Requires<Preds>;
}
// Generate cvts from all types to all types.
foreach sign = ["s", "u"] in {
defm CVT_ # sign # "8" : CVT_FROM_ALL<sign # "8", B16>;
defm CVT_ # sign # "16" : CVT_FROM_ALL<sign # "16", B16>;
defm CVT_ # sign # "32" : CVT_FROM_ALL<sign # "32", B32>;
defm CVT_ # sign # "64" : CVT_FROM_ALL<sign # "64", B64>;
}
defm CVT_f16 : CVT_FROM_ALL<"f16", B16>;
defm CVT_bf16 : CVT_FROM_ALL<"bf16", B16, [hasPTX<78>, hasSM<90>]>;
defm CVT_f32 : CVT_FROM_ALL<"f32", B32>;
defm CVT_f64 : CVT_FROM_ALL<"f64", B64>;
multiclass CVT_FROM_FLOAT_SATFINITE<string ToName, RegisterClass RC> {
def _f32_sf :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}.satfinite." # ToName # ".f32">;
}
defm CVT_bf16 : CVT_FROM_FLOAT_SATFINITE<"bf16", B16>;
defm CVT_f16 : CVT_FROM_FLOAT_SATFINITE<"f16", B16>;
// These cvts are different from those above: The source and dest registers
// are of the same type.
def CVT_INREG_s16_s8 : BasicNVPTXInst<(outs B16:$dst), (ins B16:$src), "cvt.s16.s8">;
def CVT_INREG_s32_s8 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$src), "cvt.s32.s8">;
def CVT_INREG_s32_s16 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$src), "cvt.s32.s16">;
def CVT_INREG_s64_s8 : BasicNVPTXInst<(outs B64:$dst), (ins B64:$src), "cvt.s64.s8">;
def CVT_INREG_s64_s16 : BasicNVPTXInst<(outs B64:$dst), (ins B64:$src), "cvt.s64.s16">;
def CVT_INREG_s64_s32 : BasicNVPTXInst<(outs B64:$dst), (ins B64:$src), "cvt.s64.s32">;
multiclass CVT_FROM_FLOAT_V2_SM80<string FromName, RegisterClass RC> {
def _f32 :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src1, B32:$src2), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}." # FromName # ".f32">,
Requires<[hasPTX<70>, hasSM<80>]>;
def _f32_sf :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src1, B32:$src2), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}.satfinite." # FromName # ".f32">;
}
defm CVT_f16x2 : CVT_FROM_FLOAT_V2_SM80<"f16x2", B32>;
defm CVT_bf16x2 : CVT_FROM_FLOAT_V2_SM80<"bf16x2", B32>;
multiclass CVT_FROM_FLOAT_V2_RS<string FromName, RegisterClass RC> {
def _f32_rs :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src1, B32:$src2, B32:$src3),
(ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}." # FromName # ".f32">;
def _f32_rs_sf :
BasicFlagsNVPTXInst<(outs RC:$dst),
(ins B32:$src1, B32:$src2, B32:$src3),
(ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}.satfinite." # FromName # ".f32">;
}
defm CVT_f16x2 : CVT_FROM_FLOAT_V2_RS<"f16x2", B32>;
defm CVT_bf16x2 : CVT_FROM_FLOAT_V2_RS<"bf16x2", B32>;
// FP8 conversions.
multiclass CVT_TO_F8X2<string F8Name> {
def _f32 :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src1, B32:$src2), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}." # F8Name # "x2.f32">,
Requires<[hasPTX<81>, hasSM<89>]>;
def _f16x2 :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}." # F8Name # "x2.f16x2">,
Requires<[hasPTX<81>, hasSM<89>]>;
def _bf16x2 :
BasicFlagsNVPTXInst<(outs B16:$dst), (ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}." # F8Name # "x2.bf16x2">,
Requires<[callSubtarget<"hasFP16X2ToNarrowFPConversionSupport">]>;
}
defm CVT_e4m3x2 : CVT_TO_F8X2<"e4m3">;
defm CVT_e5m2x2 : CVT_TO_F8X2<"e5m2">;
multiclass CVT_F8x2_TO_FP16x2<string F8Name> {
def f16x2_ # F8Name # x2 :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B16:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}.f16x2." # F8Name # "x2">,
Requires<[hasPTX<81>, hasSM<89>]>;
def bf16x2_ # F8Name # x2_scale :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B16:$src, B16:$src2), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}${mode:satfinite}.scaled::n2::ue8m0.bf16x2." # F8Name # "x2">,
Requires<[callSubtarget<"hasNarrowFPToBF16x2ConversionSupport">]>;
}
defm CVT_ : CVT_F8x2_TO_FP16x2<"e4m3">;
defm CVT_ : CVT_F8x2_TO_FP16x2<"e5m2">;
class CVT_TO_FP8X4<string F8Name> :
NVPTXInst<(outs B32:$dst),
(ins B32:$src1, B32:$src2, B32:$src3, B32:$src4, B32:$src5, CvtMode:$mode),
"cvt${mode:base}${mode:relu}.satfinite." # F8Name #
"x4.f32 \t$dst, {{$src1, $src2, $src3, $src4}}, $src5;">;
def CVT_e4m3x4_f32x4_rs_sf : CVT_TO_FP8X4<"e4m3">;
def CVT_e5m2x4_f32x4_rs_sf : CVT_TO_FP8X4<"e5m2">;
// Float to TF32 conversions
multiclass CVT_TO_TF32<string Modifier, list<Predicate> Preds = [hasPTX<78>, hasSM<90>]> {
defvar Intr = !cast<Intrinsic>("int_nvvm_f2tf32_" # !subst(".", "_", Modifier));
def NAME : BasicNVPTXInst<(outs B32:$dst), (ins B32:$src),
"cvt." # Modifier # ".tf32.f32",
[(set i32:$dst, (Intr f32:$src))]>,
Requires<Preds>;
}
defm CVT_to_tf32_rn : CVT_TO_TF32<"rn">;
defm CVT_to_tf32_rz : CVT_TO_TF32<"rz">;
defm CVT_to_tf32_rn_relu : CVT_TO_TF32<"rn.relu">;
defm CVT_to_tf32_rz_relu : CVT_TO_TF32<"rz.relu">;
defm CVT_to_tf32_rna : CVT_TO_TF32<"rna", [hasPTX<70>, hasSM<80>]>;
defm CVT_to_tf32_rna_satf : CVT_TO_TF32<"rna.satfinite", [hasPTX<81>, hasSM<80>]>;
defm CVT_to_tf32_rn_satf : CVT_TO_TF32<"rn.satfinite", [hasPTX<86>, hasSM<100>]>;
defm CVT_to_tf32_rz_satf : CVT_TO_TF32<"rz.satfinite", [hasPTX<86>, hasSM<100>]>;
defm CVT_to_tf32_rn_relu_satf : CVT_TO_TF32<"rn.relu.satfinite", [hasPTX<86>, hasSM<100>]>;
defm CVT_to_tf32_rz_relu_satf : CVT_TO_TF32<"rz.relu.satfinite", [hasPTX<86>, hasSM<100>]>;
let Predicates = [callSubtarget<"hasS2F6X2ConversionSupport">] in {
def CVT_s2f6x2_f32_sf_scale : BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src1, B32:$src2, B16:$scale), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}.scaled::n2::ue8m0.s2f6x2.f32">;
def CVT_s2f6x2_bf16x2_sf_scale : BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src, B16:$scale), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}.scaled::n2::ue8m0.s2f6x2.bf16x2">;
def CVT_bf16x2_s2f6x2_scale : BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B16:$src, B16:$scale), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}.scaled::n2::ue8m0.bf16x2.s2f6x2">;
def CVT_bf16x2_s2f6x2_sf_scale : BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B16:$src, B16:$scale), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}.scaled::n2::ue8m0.bf16x2.s2f6x2">;
}
// FP6 conversions.
multiclass CVT_F6x2_TO_FP16x2<string F6Name> {
def f16x2_ # F6Name # x2 :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B16:$src), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}.f16x2." # F6Name # "x2">,
Requires<[callSubtarget<"hasNarrowFPConversionSupport">]>;
def bf16x2_ # F6Name # x2_scale :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B16:$src, B16:$src2), (ins CvtMode:$mode),
"cvt${mode:base}${mode:relu}${mode:satfinite}.scaled::n2::ue8m0.bf16x2." # F6Name # "x2">,
Requires<[callSubtarget<"hasNarrowFPToBF16x2ConversionSupport">]>;
}
defm CVT_ : CVT_F6x2_TO_FP16x2<"e2m3">;
defm CVT_ : CVT_F6x2_TO_FP16x2<"e3m2">;
class CVT_TO_FP6X4<string F6Name> :
NVPTXInst<(outs B32:$dst),
(ins B32:$src1, B32:$src2, B32:$src3, B32:$src4, B32:$src5, CvtMode:$mode),
"cvt${mode:base}${mode:relu}.satfinite." # F6Name #
"x4.f32 \t$dst, {{$src1, $src2, $src3, $src4}}, $src5;">;
def CVT_e2m3x4_f32x4_rs_sf : CVT_TO_FP6X4<"e2m3">;
def CVT_e3m2x4_f32x4_rs_sf : CVT_TO_FP6X4<"e3m2">;
multiclass CVT_TO_FP6X2<string FP6Name> {
def _f32_sf :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src1, B32:$src2), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}." # FP6Name # "x2.f32">;
def _f16x2_sf :
BasicFlagsNVPTXInst<(outs B16:$dst), (ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}." # FP6Name # "x2.f16x2">,
Requires<[callSubtarget<"hasFP16X2ToNarrowFPConversionSupport">]>;
def _bf16x2_sf :
BasicFlagsNVPTXInst<(outs B16:$dst), (ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}.satfinite${mode:relu}." # FP6Name # "x2.bf16x2">,
Requires<[callSubtarget<"hasFP16X2ToNarrowFPConversionSupport">]>;
}
defm CVT_e2m3x2 : CVT_TO_FP6X2<"e2m3">;
defm CVT_e3m2x2 : CVT_TO_FP6X2<"e3m2">;
// FP4 conversions.
def CVT_e2m1x2_f32_sf : NVPTXInst<(outs B16:$dst),
(ins B32:$src1, B32:$src2, CvtMode:$mode),
!strconcat("{{ \n\t",
".reg .b8 \t%e2m1x2_out; \n\t",
"cvt${mode:base}.satfinite${mode:relu}.e2m1x2.f32 \t%e2m1x2_out, $src1, $src2; \n\t",
"cvt.u16.u8 \t$dst, %e2m1x2_out; \n\t",
"}}"), []>;
def CVT_f16x2_e2m1x2 : NVPTXInst<(outs B32:$dst),
(ins B16:$src, CvtMode:$mode),
!strconcat("{{ \n\t",
".reg .b8 \t%e2m1x2_in; \n\t",
"cvt.u8.u16 \t%e2m1x2_in, $src; \n\t",
"cvt${mode:base}${mode:relu}.f16x2.e2m1x2 \t$dst, %e2m1x2_in; \n\t",
"}}"), []>;
def CVT_bf16x2_e2m1x2_scale :
NVPTXInst<(outs B32:$dst),
(ins B16:$src1, B16:$src2, CvtMode:$mode),
"{{ \n\t" #
".reg .b8 \t%e2m1x2_in; \n\t" #
"cvt.u8.u16 \t%e2m1x2_in, $src1; \n\t" #
"cvt${mode:base}${mode:relu}${mode:satfinite}.scaled::n2::ue8m0.bf16x2.e2m1x2 \t$dst, %e2m1x2_in, $src2; \n\t" #
"}}", []>,
Requires<[callSubtarget<"hasNarrowFPToBF16x2ConversionSupport">]>;
def CVT_e2m1x4_f32x4_rs_sf :
NVPTXInst<(outs B16:$dst),
(ins B32:$src1, B32:$src2, B32:$src3, B32:$src4, B32:$src5, CvtMode:$mode),
"cvt${mode:base}${mode:relu}.satfinite.e2m1x4.f32 \t" #
"$dst, {{$src1, $src2, $src3, $src4}}, $src5;">;
def CVT_e2m1x2_f16x2_sf : NVPTXInst<(outs B16:$dst),
(ins B32:$src, CvtMode:$mode),
"{{ \n\t" #
".reg .b8 \t%e2m1x2_out; \n\t" #
"cvt${mode:base}.satfinite${mode:relu}.e2m1x2.f16x2 \t%e2m1x2_out, $src; \n\t" #
"cvt.u16.u8 \t$dst, %e2m1x2_out; \n\t" #
"}}", []>,
Requires<[callSubtarget<"hasFP16X2ToNarrowFPConversionSupport">]>;
def CVT_e2m1x2_bf16x2_sf : NVPTXInst<(outs B16:$dst),
(ins B32:$src, CvtMode:$mode),
"{{ \n\t" #
".reg .b8 \t%e2m1x2_out; \n\t" #
"cvt${mode:base}.satfinite${mode:relu}.e2m1x2.bf16x2 \t%e2m1x2_out, $src; \n\t" #
"cvt.u16.u8 \t$dst, %e2m1x2_out; \n\t" #
"}}", []>,
Requires<[callSubtarget<"hasFP16X2ToNarrowFPConversionSupport">]>;
// UE8M0x2 conversions.
class CVT_f32_to_ue8m0x2<string sat = ""> :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src1, B32:$src2), (ins CvtMode:$mode),
"cvt${mode:base}" # sat # ".ue8m0x2.f32">;
class CVT_bf16x2_to_ue8m0x2<string sat = ""> :
BasicFlagsNVPTXInst<(outs B16:$dst),
(ins B32:$src), (ins CvtMode:$mode),
"cvt${mode:base}" # sat # ".ue8m0x2.bf16x2">;
def CVT_ue8m0x2_f32 : CVT_f32_to_ue8m0x2;
def CVT_ue8m0x2_f32_sf : CVT_f32_to_ue8m0x2<".satfinite">;
def CVT_ue8m0x2_bf16x2 : CVT_bf16x2_to_ue8m0x2;
def CVT_ue8m0x2_bf16x2_sf : CVT_bf16x2_to_ue8m0x2<".satfinite">;
def CVT_bf16x2_ue8m0x2 :
BasicNVPTXInst<(outs B32:$dst),
(ins B16:$src),
"cvt.rn.bf16x2.ue8m0x2">;
}
def fpround_oneuse : OneUse1<fpround>;
def : Pat<(v2bf16 (build_vector (bf16 (fpround_oneuse f32:$lo)),
(bf16 (fpround_oneuse f32:$hi)))),
(CVT_bf16x2_f32 $hi, $lo, CvtRN)>,
Requires<[hasPTX<70>, hasSM<80>, hasBF16Math]>;
def : Pat<(v2f16 (build_vector (f16 (fpround_oneuse f32:$lo)),
(f16 (fpround_oneuse f32:$hi)))),
(CVT_f16x2_f32 $hi, $lo, CvtRN)>,
Requires<[hasPTX<70>, hasSM<80>, useFP16Math]>;
//-----------------------------------
// Selection instructions (selp)
//-----------------------------------
// TODO: Missing slct
// selp instructions that don't have any pattern matches; we explicitly use
// them within this file.
let hasSideEffects = false in {
multiclass SELP_PATTERN<string TypeStr, RegTyInfo t> {
defvar asm_str = "selp." # TypeStr;
def rr :
BasicNVPTXInst<(outs t.RC:$dst),
(ins t.RC:$a, t.RC:$b, B1:$p),
asm_str,
[(set t.Ty:$dst, (select i1:$p, t.Ty:$a, t.Ty:$b))]>;
def ri :
BasicNVPTXInst<(outs t.RC:$dst),
(ins t.RC:$a, t.Imm:$b, B1:$p),
asm_str,
[(set t.Ty:$dst, (select i1:$p, t.Ty:$a, t.ImmNode:$b))]>;
def ir :
BasicNVPTXInst<(outs t.RC:$dst),
(ins t.Imm:$a, t.RC:$b, B1:$p),
asm_str,
[(set t.Ty:$dst, (select i1:$p, t.ImmNode:$a, t.Ty:$b))]>;
def ii :
BasicNVPTXInst<(outs t.RC:$dst),
(ins t.Imm:$a, t.Imm:$b, B1:$p),
asm_str,
[(set t.Ty:$dst, (select i1:$p, t.ImmNode:$a, t.ImmNode:$b))]>;
}
}
// Don't pattern match on selp.{s,u}{16,32,64} -- selp.b{16,32,64} is just as
// good.
defm SELP_b16 : SELP_PATTERN<"b16", I16RT>;
defm SELP_b32 : SELP_PATTERN<"b32", I32RT>;
defm SELP_b64 : SELP_PATTERN<"b64", I64RT>;
defm SELP_f16 : SELP_PATTERN<"b16", F16RT>;
defm SELP_bf16 : SELP_PATTERN<"b16", BF16RT>;
defm SELP_f32 : SELP_PATTERN<"f32", F32RT>;
defm SELP_f64 : SELP_PATTERN<"f64", F64RT>;
// This does not work as tablegen fails to infer the type of 'imm'.
// def v2f16imm : Operand<v2f16>;
// defm SELP_f16x2 : SELP_PATTERN<"b32", v2f16, B32, v2f16imm, imm>;
foreach vt = [v2f16, v2bf16, v2i16, v4i8] in {
def : Pat<(vt (select i1:$p, vt:$a, vt:$b)),
(SELP_b32rr $a, $b, $p)>;
}
foreach vt = [v2f32, v2i32] in {
def : Pat<(vt (select i1:$p, vt:$a, vt:$b)),
(SELP_b64rr $a, $b, $p)>;
}
//-----------------------------------
// Test Instructions
//-----------------------------------
def fabs_oneuse : OneUse1<fabs>;
def TESTINF_f32r : BasicNVPTXInst<(outs B1:$p), (ins B32:$a),
"testp.infinite.f32",
[(set i1:$p, (seteq (fabs_oneuse f32:$a), fpimm_pos_inf))]>;
def TESTINF_f64r : BasicNVPTXInst<(outs B1:$p), (ins B64:$a),
"testp.infinite.f64",
[(set i1:$p, (seteq (fabs_oneuse f64:$a), fpimm_pos_inf))]>;
//-----------------------------------
// Integer Arithmetic
//-----------------------------------
// int16, int32, and int64 signed addition. Since nvptx is 2's complement, we
// also use these for unsigned arithmetic.
defm ADD : I3<"add.s", add, commutative = true>;
defm SUB : I3<"sub.s", sub, commutative = false>;
def ADD16x2 : I16x2<"add.s", add>;
// int32 and int64 addition and subtraction with carry-out.
defm ADDCC : ADD_SUB_INT_CARRY<"add.cc", addc, commutative = true>;
defm SUBCC : ADD_SUB_INT_CARRY<"sub.cc", subc, commutative = false>;
// int32 and int64 addition and subtraction with carry-in and carry-out.
defm ADDCCC : ADD_SUB_INT_CARRY<"addc.cc", adde, commutative = true>;
defm SUBCCC : ADD_SUB_INT_CARRY<"subc.cc", sube, commutative = false>;
defm MULT : I3<"mul.lo.s", mul, commutative = true>;
defm MUL_HI_S : I3<"mul.hi.s", mulhs, commutative = true>;
defm MUL_HI_U : I3<"mul.hi.u", mulhu, commutative = true>;
defm SDIV : I3<"div.s", sdiv, commutative = false>;
defm UDIV : I3<"div.u", udiv, commutative = false>;
// The ri versions of rem.s and rem.u won't be selected; DAGCombiner::visitSREM
// will lower it.
defm SREM : I3<"rem.s", srem, commutative = false>;
defm UREM : I3<"rem.u", urem, commutative = false>;
foreach t = [I16RT, I32RT, I64RT] in {
// PTX abs.s is undefined for INT_MIN, so it matches ABS_MIN_POISON
// semantics (where INT_MIN input is poison). ISD::ABS requires
// abs(INT_MIN) == INT_MIN and is expanded separately.
def ABS_S # t.Size :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a),
"abs.s" # t.Size,
[(set t.Ty:$dst, (abs_min_poison t.Ty:$a))]>;
def NEG_S # t.Size :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$src),
"neg.s" # t.Size,
[(set t.Ty:$dst, (ineg t.Ty:$src))]>;
}
// Integer min/max.
defm SMAX : I3<"max.s", smax, commutative = true>;
defm UMAX : I3<"max.u", umax, commutative = true>;
defm SMIN : I3<"min.s", smin, commutative = true>;
defm UMIN : I3<"min.u", umin, commutative = true>;
def SMAX16x2 : I16x2<"max.s", smax>;
def UMAX16x2 : I16x2<"max.u", umax>;
def SMIN16x2 : I16x2<"min.s", smin>;
def UMIN16x2 : I16x2<"min.u", umin>;
let Predicates = [hasPTX<80>, hasSM<90>] in {
def MIN_RELU_S32 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$a, B32:$b),
"min.relu.s32",
[(set i32:$dst, (smax (smin i32:$a, i32:$b), 0))]>;
def MAX_RELU_S32 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$a, B32:$b),
"max.relu.s32",
[(set i32:$dst, (smax (smax i32:$a, i32:$b), 0))]>;
def MIN_RELU_S16x2 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$a, B32:$b),
"min.relu.s16x2",
[(set v2i16:$dst, (smax (smin v2i16:$a, v2i16:$b),
zeroinitializer<v2i16>))]>;
def MAX_RELU_S16x2 : BasicNVPTXInst<(outs B32:$dst), (ins B32:$a, B32:$b),
"max.relu.s16x2",
[(set v2i16:$dst, (smax (smax v2i16:$a, v2i16:$b),
zeroinitializer<v2i16>))]>;
}
//
// Wide multiplication
//
def SDTMulWide : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisInt<1>, SDTCisSameAs<1, 2>]>;
def smul_wide : SDNode<"NVPTXISD::MUL_WIDE_SIGNED", SDTMulWide, [SDNPCommutative]>;
def umul_wide : SDNode<"NVPTXISD::MUL_WIDE_UNSIGNED", SDTMulWide, [SDNPCommutative]>;
multiclass MULWIDEInst<string suffix, SDPatternOperator op, RegTyInfo big_t, RegTyInfo small_t> {
def suffix # _rr :
BasicNVPTXInst<(outs big_t.RC:$dst), (ins small_t.RC:$a, small_t.RC:$b),
"mul.wide." # suffix,
[(set big_t.Ty:$dst, (op small_t.Ty:$a, small_t.Ty:$b))]>;
def suffix # _ri :
BasicNVPTXInst<(outs big_t.RC:$dst), (ins small_t.RC:$a, small_t.Imm:$b),
"mul.wide." # suffix,
[(set big_t.Ty:$dst, (op small_t.Ty:$a, imm:$b))]>;
}
defm MUL_WIDE : MULWIDEInst<"s32", smul_wide, I64RT, I32RT>;
defm MUL_WIDE : MULWIDEInst<"u32", umul_wide, I64RT, I32RT>;
defm MUL_WIDE : MULWIDEInst<"s16", smul_wide, I32RT, I16RT>;
defm MUL_WIDE : MULWIDEInst<"u16", umul_wide, I32RT, I16RT>;
//
// Integer multiply-add
//
multiclass MADInst<string suffix, SDPatternOperator op, RegTyInfo big_t, RegTyInfo small_t> {
def rrr:
BasicNVPTXInst<(outs big_t.RC:$dst),
(ins small_t.RC:$a, small_t.RC:$b, big_t.RC:$c),
"mad." # suffix,
[(set big_t.Ty:$dst, (add (OneUse2<op> small_t.Ty:$a, small_t.Ty:$b), big_t.Ty:$c))]>;
def rri:
BasicNVPTXInst<(outs big_t.RC:$dst),
(ins small_t.RC:$a, small_t.RC:$b, big_t.Imm:$c),
"mad." # suffix,
[(set big_t.Ty:$dst, (add (OneUse2<op> small_t.Ty:$a, small_t.Ty:$b), imm:$c))]>;
def rir:
BasicNVPTXInst<(outs big_t.RC:$dst),
(ins small_t.RC:$a, small_t.Imm:$b, big_t.RC:$c),
"mad." # suffix,
[(set big_t.Ty:$dst, (add (OneUse2<op> small_t.Ty:$a, imm:$b), big_t.Ty:$c))]>;
def rii:
BasicNVPTXInst<(outs big_t.RC:$dst),
(ins small_t.RC:$a, small_t.Imm:$b, big_t.Imm:$c),
"mad." # suffix,
[(set big_t.Ty:$dst, (add (OneUse2<op> small_t.Ty:$a, imm:$b), imm:$c))]>;
}
let Predicates = [hasOptEnabled] in {
defm MAD_LO_S16 : MADInst<"lo.s16", mul, I16RT, I16RT>;
defm MAD_LO_S32 : MADInst<"lo.s32", mul, I32RT, I32RT>;
defm MAD_LO_S64 : MADInst<"lo.s64", mul, I64RT, I64RT>;
// Generating mad.wide causes a regression in some cases:
// https://github.com/llvm/llvm-project/pull/150477#issuecomment-3191367837
// Only do so when the user requests it.
let Predicates = [doMADWideOpt] in {
defm MAD_WIDE_U16 : MADInst<"wide.u16", umul_wide, I32RT, I16RT>;
defm MAD_WIDE_S16 : MADInst<"wide.s16", smul_wide, I32RT, I16RT>;
defm MAD_WIDE_U32 : MADInst<"wide.u32", umul_wide, I64RT, I32RT>;
defm MAD_WIDE_S32 : MADInst<"wide.s32", smul_wide, I64RT, I32RT>;
}
}
//-----------------------------------
// Floating Point Arithmetic
//-----------------------------------
defm FADD : F3_fma_component<"add", fadd>;
defm FSUB : F3_fma_component<"sub", fsub>;
defm FMUL : F3_fma_component<"mul", fmul>;
def fminnum_or_fminimumnum : PatFrags<(ops node:$a, node:$b),
[(fminnum node:$a, node:$b),
(fminimumnum node:$a, node:$b)]>;
def fmaxnum_or_fmaximumnum : PatFrags<(ops node:$a, node:$b),
[(fmaxnum node:$a, node:$b),
(fmaximumnum node:$a, node:$b)]>;
defm MIN : FMINIMUMMAXIMUM<"min", /* NaN */ false, fminnum_or_fminimumnum>;
defm MAX : FMINIMUMMAXIMUM<"max", /* NaN */ false, fmaxnum_or_fmaximumnum>;
defm MIN_NAN : FMINIMUMMAXIMUM<"min", /* NaN */ true, fminimum>;
defm MAX_NAN : FMINIMUMMAXIMUM<"max", /* NaN */ true, fmaximum>;
def nvptx_fminnum3 : SDNode<"NVPTXISD::FMINNUM3", SDTFPTernaryOp,
[SDNPCommutative]>;
def nvptx_fmaxnum3 : SDNode<"NVPTXISD::FMAXNUM3", SDTFPTernaryOp,
[SDNPCommutative]>;
def nvptx_fminimum3 : SDNode<"NVPTXISD::FMINIMUM3", SDTFPTernaryOp,
[SDNPCommutative]>;
def nvptx_fmaximum3 : SDNode<"NVPTXISD::FMAXIMUM3", SDTFPTernaryOp,
[SDNPCommutative]>;
defm FMIN3 : FMINIMUMMAXIMUM3<"min", /* NaN */ false, nvptx_fminnum3>;
defm FMAX3 : FMINIMUMMAXIMUM3<"max", /* NaN */ false, nvptx_fmaxnum3>;
defm FMINNAN3 : FMINIMUMMAXIMUM3<"min", /* NaN */ true, nvptx_fminimum3>;
defm FMAXNAN3 : FMINIMUMMAXIMUM3<"max", /* NaN */ true, nvptx_fmaximum3>;
defm FABS : F2<"abs", fabs>;
defm FNEG : F2<"neg", fneg>;
defm FABS_H: F2_Support_Half<"abs", fabs>;
defm FNEG_H: F2_Support_Half<"neg", fneg>;
defm FSQRT : F2<"sqrt.rn", fsqrt>;
//
// F16 NEG
//
class FNEG16<RegTyInfo t> :
BasicFlagsNVPTXInst<(outs t.RC:$dst), (ins t.RC:$src), (ins FTZFlag:$ftz),
"neg$ftz." # t.PtxType,
[(set t.Ty:$dst, (fneg t.Ty:$src))]>;
let Predicates = [useFP16Math, hasPTX<60>, hasSM<53>] in {
def NEG_F16 : FNEG16<F16RT>;
def NEG_F16x2 : FNEG16<F16X2RT>;
}
let Predicates = [hasBF16Math, hasPTX<70>, hasSM<80>] in {
def NEG_BF16 : FNEG16<BF16RT>;
def NEG_BF16x2 : FNEG16<BF16X2RT>;
}
//
// EX2
//
class FEXP2Inst<RegTyInfo t, dag flags, string flag_str> :
BasicFlagsNVPTXInst<(outs t.RC:$dst), (ins t.RC:$src),
flags, "ex2.approx" # flag_str # "." # t.PtxType,
[(set t.Ty:$dst, (fexp2 t.Ty:$src))]>;
def EX2_APPROX_f32 : FEXP2Inst<F32RT, (ins FTZFlag:$ftz), "$ftz">;
let Predicates = [useFP16Math, hasPTX<70>, hasSM<75>] in {
def EX2_APPROX_f16 : FEXP2Inst<F16RT, (ins), "">;
def EX2_APPROX_f16x2 : FEXP2Inst<F16X2RT, (ins), "">;
}
let Predicates = [hasPTX<78>, hasSM<90>] in {
def EX2_APPROX_bf16 : FEXP2Inst<BF16RT, (ins), ".ftz">;
def EX2_APPROX_bf16x2 : FEXP2Inst<BF16X2RT, (ins), ".ftz">;
}
// F64 division
//
def FRCP64r :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$b),
"rcp.rn.f64",
[(set f64:$dst, (fdiv fpimm_1, f64:$b))]>;
def FDIV64rr :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$a, B64:$b),
"div.rn.f64",
[(set f64:$dst, (fdiv f64:$a, f64:$b))]>;
def FDIV64ri :
BasicNVPTXInst<(outs B64:$dst),
(ins B64:$a, f64imm:$b),
"div.rn.f64",
[(set f64:$dst, (fdiv f64:$a, fpimm:$b))]>;
// fdiv will be converted to rcp
// fneg (fdiv 1.0, X) => fneg (rcp.rn X)
def : Pat<(fdiv fpimm_neg_1, f64:$b),
(FNEGf64 (FRCP64r $b))>;
//
// F32 Approximate reciprocal
//
def fdiv_approx : PatFrag<(ops node:$a, node:$b),
(fdiv node:$a, node:$b), [{
return getDivF32Level(N) == NVPTX::DivPrecisionLevel::Approx;
}]>;
def RCP_APPROX_F32_r :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$b), (ins FTZFlag:$ftz),
"rcp.approx$ftz.f32",
[(set f32:$dst, (fdiv_approx fpimm_1, f32:$b))]>;
//
// F32 Approximate division
//
def DIV_APPROX_F32_rr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b), (ins FTZFlag:$ftz),
"div.approx$ftz.f32",
[(set f32:$dst, (fdiv_approx f32:$a, f32:$b))]>;
def DIV_APPROX_F32_ri :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, f32imm:$b), (ins FTZFlag:$ftz),
"div.approx$ftz.f32",
[(set f32:$dst, (fdiv_approx f32:$a, fpimm:$b))]>;
//
// F32 Semi-accurate reciprocal
//
// rcp.approx gives the same result as div.full(1.0f, a) and is faster.
//
def fdiv_full : PatFrag<(ops node:$a, node:$b),
(fdiv node:$a, node:$b), [{
return getDivF32Level(N) == NVPTX::DivPrecisionLevel::Full;
}]>;
def : Pat<(fdiv_full fpimm_1, f32:$b),
(RCP_APPROX_F32_r $b)>;
//
// F32 Semi-accurate division
//
def FDIV32rr :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b), (ins FTZFlag:$ftz),
"div.full$ftz.f32",
[(set f32:$dst, (fdiv_full f32:$a, f32:$b))]>;
def FDIV32ri :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, f32imm:$b), (ins FTZFlag:$ftz),
"div.full$ftz.f32",
[(set f32:$dst, (fdiv_full f32:$a, fpimm:$b))]>;
//
// F32 Accurate reciprocal
//
def fdiv_ftz : PatFrag<(ops node:$a, node:$b),
(fdiv node:$a, node:$b), [{
return getDivF32Level(N) == NVPTX::DivPrecisionLevel::IEEE754;
}]>;
def FRCP32r_prec :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$b), (ins FTZFlag:$ftz),
"rcp.rn$ftz.f32",
[(set f32:$dst, (fdiv_ftz fpimm_1, f32:$b))]>;
//
// F32 Accurate division
//
def FDIV32rr_prec :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b), (ins FTZFlag:$ftz),
"div.rn$ftz.f32",
[(set f32:$dst, (fdiv_ftz f32:$a, f32:$b))]>;
def FDIV32ri_prec :
BasicFlagsNVPTXInst<(outs B32:$dst),
(ins B32:$a, f32imm:$b), (ins FTZFlag:$ftz),
"div.rn$ftz.f32",
[(set f32:$dst, (fdiv_ftz f32:$a, fpimm:$b))]>;
def : Pat<(fdiv fpimm_1, f32:$b), (FRCP32r_prec $b, NoFTZ)>;
def : Pat<(fdiv f32:$a, f32:$b), (FDIV32rr_prec $a, $b, NoFTZ)>;
def : Pat<(fdiv f32:$a, fpimm:$b), (FDIV32ri_prec $a, fpimm:$b, NoFTZ)>;
//
// FMA
//
multiclass FMA<RegTyInfo t, bit allow_ftz = true, list<Predicate> preds = []> {
defvar flag_str = !if(allow_ftz, "$ftz", "");
defvar flag_ops = !if(allow_ftz, (ins FTZFlag:$ftz), (ins));
defvar op_str = "fma.rn" # flag_str # "." # t.PtxType;
let Predicates = preds in {
def rrr : BasicFlagsNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, t.RC:$b, t.RC:$c),
flag_ops, op_str,
[(set t.Ty:$dst, (fma t.Ty:$a, t.Ty:$b, t.Ty:$c))]>;
if t.SupportsImm then {
def rri : BasicFlagsNVPTXInst<(outs t.RC:$dst),
(ins t.RC:$a, t.RC:$b, t.Imm:$c),
flag_ops, op_str,
[(set t.Ty:$dst, (fma t.Ty:$a, t.Ty:$b, fpimm:$c))]>;
def rir : BasicFlagsNVPTXInst<(outs t.RC:$dst),
(ins t.RC:$a, t.Imm:$b, t.RC:$c),
flag_ops, op_str,
[(set t.Ty:$dst, (fma t.Ty:$a, fpimm:$b, t.Ty:$c))]>;
def rii : BasicFlagsNVPTXInst<(outs t.RC:$dst),
(ins t.RC:$a, t.Imm:$b, t.Imm:$c),
flag_ops, op_str,
[(set t.Ty:$dst, (fma t.Ty:$a, fpimm:$b, fpimm:$c))]>;
def iir : BasicFlagsNVPTXInst<(outs t.RC:$dst),
(ins t.Imm:$a, t.Imm:$b, t.RC:$c),
flag_ops, op_str,
[(set t.Ty:$dst, (fma fpimm:$a, fpimm:$b, t.Ty:$c))]>;
}
}
}
defm FMA_F16 : FMA<F16RT, allow_ftz = true, preds = [useFP16Math]>;
defm FMA_F16x2 : FMA<F16X2RT, allow_ftz = true, preds = [useFP16Math]>;
defm FMA_BF16 : FMA<BF16RT, allow_ftz = false, preds = [hasBF16Math]>;
defm FMA_BF16x2 : FMA<BF16X2RT, allow_ftz = false, preds = [hasBF16Math]>;
defm FMA_F32 : FMA<F32RT, allow_ftz = true>;
defm FMA_F32x2 : FMA<F32X2RT, allow_ftz = true, preds = [hasF32x2Instructions]>;
defm FMA_F64 : FMA<F64RT, allow_ftz = false>;
// sin/cos/tanh
class UnaryOpAllowsApproxFn<SDPatternOperator operator>
: PatFrag<(ops node:$A), (operator node:$A), [{
return N->getFlags().hasApproximateFuncs();
}]>;
def SIN_APPROX_f32 :
BasicFlagsNVPTXInst<(outs B32:$dst), (ins B32:$src), (ins FTZFlag:$ftz),
"sin.approx$ftz.f32",
[(set f32:$dst, (UnaryOpAllowsApproxFn<fsin> f32:$src))]>;
def COS_APPROX_f32 :
BasicFlagsNVPTXInst<(outs B32:$dst), (ins B32:$src), (ins FTZFlag:$ftz),
"cos.approx$ftz.f32",
[(set f32:$dst, (UnaryOpAllowsApproxFn<fcos> f32:$src))]>;
def TANH_APPROX_f32 :
BasicNVPTXInst<(outs B32:$dst), (ins B32:$src), "tanh.approx.f32",
[(set f32:$dst, (UnaryOpAllowsApproxFn<ftanh> f32:$src))]>,
Requires<[hasPTX<70>, hasSM<75>]>;
//-----------------------------------
// Bitwise operations
//-----------------------------------
// Template for three-arg bitwise operations. Takes three args, Creates .b16,
// .b32, .b64, and .pred (predicate registers -- i.e., i1) versions of OpcStr.
multiclass BITWISE<string OpcStr, SDNode OpNode> {
foreach t = [I1RT, I16RT, I32RT, I64RT] in
defm _ # t.PtxType : I3Inst<OpcStr # "." # t.PtxType, OpNode, t, commutative = true>;
}
defm OR : BITWISE<"or", or>;
defm AND : BITWISE<"and", and>;
defm XOR : BITWISE<"xor", xor>;
// PTX does not support mul on predicates, convert to and instructions
def : Pat<(mul i1:$a, i1:$b), (AND_predrr $a, $b)>;
def : Pat<(mul i1:$a, imm:$b), (AND_predri $a, imm:$b)>;
foreach op = [add, sub] in {
def : Pat<(op i1:$a, i1:$b), (XOR_predrr $a, $b)>;
def : Pat<(op i1:$a, imm:$b), (XOR_predri $a, imm:$b)>;
}
// These transformations were once reliably performed by instcombine, but thanks
// to poison semantics they are no longer safe for LLVM IR, perform them here
// instead.
def : Pat<(select i1:$a, i1:$b, 0), (AND_predrr $a, $b)>;
def : Pat<(select i1:$a, 1, i1:$b), (OR_predrr $a, $b)>;
// Lower logical v2i16/v4i8 ops as bitwise ops on b32.
foreach vt = [v2i16, v4i8] in {
def : Pat<(or vt:$a, vt:$b), (OR_b32rr $a, $b)>;
def : Pat<(xor vt:$a, vt:$b), (XOR_b32rr $a, $b)>;
def : Pat<(and vt:$a, vt:$b), (AND_b32rr $a, $b)>;
// The constants get legalized into a bitcast from i32, so that's what we need
// to match here.
def: Pat<(or vt:$a, (vt (bitconvert (i32 imm:$b)))),
(OR_b32ri $a, imm:$b)>;
def: Pat<(xor vt:$a, (vt (bitconvert (i32 imm:$b)))),
(XOR_b32ri $a, imm:$b)>;
def: Pat<(and vt:$a, (vt (bitconvert (i32 imm:$b)))),
(AND_b32ri $a, imm:$b)>;
}
foreach t = [I1RT, I16RT, I32RT, I64RT] in
def NOT_ # t.PtxType : BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$src),
"not." # t.PtxType,
[(set t.Ty:$dst, (not t.Ty:$src))]>;
// Template for left/right shifts. Takes three operands,
// [dest (reg), src (reg), shift (reg or imm)].
// dest and src may be int64, int32, or int16, but shift is always int32.
//
// This template also defines a 32-bit shift (imm, imm) instruction.
multiclass SHIFT<string OpcStr, SDNode OpNode> {
let hasSideEffects = false in {
foreach t = [I64RT, I32RT, I16RT] in {
def t.Size # _rr :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, B32:$b),
OpcStr # t.Size,
[(set t.Ty:$dst, (OpNode t.Ty:$a, i32:$b))]>;
def t.Size # _ri :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, i32imm:$b),
OpcStr # t.Size,
[(set t.Ty:$dst, (OpNode t.Ty:$a, (i32 imm:$b)))]>;
def t.Size # _ii :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, i32imm:$b),
OpcStr # t.Size,
[(set t.Ty:$dst, (OpNode (t.Ty imm:$a), (i32 imm:$b)))]>;
}
}
}
defm SHL : SHIFT<"shl.b", shl>;
defm SRA : SHIFT<"shr.s", sra>;
defm SRL : SHIFT<"shr.u", srl>;
// Shift with clamping semantics - these have defined behavior for shift amounts
// >= BitWidth (returning 0 for logical shifts). Used to optimize guarded shift
// patterns like `shift >= 32 ? 0 : x >> shift`.
def shl_clamp : SDNode<"NVPTXISD::SHL_CLAMP", SDTIntShiftOp, []>;
def srl_clamp : SDNode<"NVPTXISD::SRL_CLAMP", SDTIntShiftOp, []>;
defm SHL_CLAMP : SHIFT<"shl.b", shl_clamp>;
defm SRL_CLAMP : SHIFT<"shr.u", srl_clamp>;
// Bit-reverse
foreach t = [I64RT, I32RT] in
def BREV_ # t.PtxType :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a),
"brev." # t.PtxType,
[(set t.Ty:$dst, (bitreverse t.Ty:$a))]>;
//
// BFE - bit-field extract
//
// Template for BFE/BFI instructions.
// Args: [dest (reg), src (reg), start (reg or imm), end (reg or imm)].
// Start may be an imm only if end is also an imm. FIXME: Is this a
// restriction in PTX?
//
// dest and src may be int32 or int64, but start and end are always int32.
def SDTBFI :
SDTypeProfile<1, 4, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>,
SDTCisVT<3, i32>, SDTCisVT<4, i32>]>;
def bfi : SDNode<"NVPTXISD::BFI", SDTBFI>;
def SDTPRMT :
SDTypeProfile<1, 4, [SDTCisVT<0, i32>, SDTCisVT<1, i32>,
SDTCisVT<2, i32>, SDTCisVT<3, i32>, SDTCisVT<4, i32>]>;
def prmt : SDNode<"NVPTXISD::PRMT", SDTPRMT>;
multiclass BFE<string Instr, RegisterClass RC> {
def rrr
: BasicNVPTXInst<(outs RC:$d), (ins RC:$a, B32:$b, B32:$c), Instr>;
def rri
: BasicNVPTXInst<(outs RC:$d), (ins RC:$a, B32:$b, i32imm:$c), Instr>;
def rii
: BasicNVPTXInst<(outs RC:$d), (ins RC:$a, i32imm:$b, i32imm:$c), Instr>;
}
multiclass BFI<string Instr, ValueType T, RegisterClass RC, Operand ImmCls> {
def rrrr
: BasicNVPTXInst<(outs RC:$f),
(ins RC:$a, RC:$b, B32:$c, B32:$d),
Instr,
[(set T:$f, (bfi T:$a, T:$b, i32:$c, i32:$d))]>;
def rrri
: BasicNVPTXInst<(outs RC:$f),
(ins RC:$a, RC:$b, B32:$c, i32imm:$d),
Instr,
[(set T:$f, (bfi T:$a, T:$b, i32:$c, imm:$d))]>;
def rrii
: BasicNVPTXInst<(outs RC:$f),
(ins RC:$a, RC:$b, i32imm:$c, i32imm:$d),
Instr,
[(set T:$f, (bfi T:$a, T:$b, imm:$c, imm:$d))]>;
def irrr
: BasicNVPTXInst<(outs RC:$f),
(ins ImmCls:$a, RC:$b, B32:$c, B32:$d),
Instr,
[(set T:$f, (bfi (T imm:$a), T:$b, i32:$c, i32:$d))]>;
def irri
: BasicNVPTXInst<(outs RC:$f),
(ins ImmCls:$a, RC:$b, B32:$c, i32imm:$d),
Instr,
[(set T:$f, (bfi (T imm:$a), T:$b, i32:$c, imm:$d))]>;
def irii
: BasicNVPTXInst<(outs RC:$f),
(ins ImmCls:$a, RC:$b, i32imm:$c, i32imm:$d),
Instr,
[(set T:$f, (bfi (T imm:$a), T:$b, imm:$c, imm:$d))]>;
}
def Hexu32imm : Operand<i32> {
let PrintMethod = "printHexu32imm";
}
let hasSideEffects = false in {
// order is somewhat important here. signed/unsigned variants match
// the same patterns, so the first one wins. Having unsigned byte extraction
// has the benefit of always having zero in unused bits, which makes some
// optimizations easier (e.g. no need to mask them).
defm BFE_U32 : BFE<"bfe.u32", B32>;
defm BFE_S32 : BFE<"bfe.s32", B32>;
defm BFE_U64 : BFE<"bfe.u64", B64>;
defm BFE_S64 : BFE<"bfe.s64", B64>;
defm BFI_B32 : BFI<"bfi.b32", i32, B32, i32imm>;
defm BFI_B64 : BFI<"bfi.b64", i64, B64, i64imm>;
def PRMT_B32rrr
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins B32:$a, B32:$b, B32:$c),
(ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt i32:$a, i32:$b, i32:$c, imm:$mode))]>;
def PRMT_B32rri
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins B32:$a, B32:$b, Hexu32imm:$c),
(ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt i32:$a, i32:$b, imm:$c, imm:$mode))]>;
def PRMT_B32rir
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins B32:$a, i32imm:$b, B32:$c),
(ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt i32:$a, imm:$b, i32:$c, imm:$mode))]>;
def PRMT_B32rii
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins B32:$a, i32imm:$b, Hexu32imm:$c),
(ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt i32:$a, imm:$b, imm:$c, imm:$mode))]>;
def PRMT_B32irr
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins i32imm:$a, B32:$b, B32:$c), (ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt imm:$a, i32:$b, i32:$c, imm:$mode))]>;
def PRMT_B32iri
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins i32imm:$a, B32:$b, Hexu32imm:$c), (ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt imm:$a, i32:$b, imm:$c, imm:$mode))]>;
def PRMT_B32iir
: BasicFlagsNVPTXInst<(outs B32:$d),
(ins i32imm:$a, i32imm:$b, B32:$c), (ins PrmtMode:$mode),
"prmt.b32$mode",
[(set i32:$d, (prmt imm:$a, imm:$b, i32:$c, imm:$mode))]>;
}
// PRMT folding patterns
def : Pat<(fshr i32:$hi, i32:$lo, (shl i32:$amt, (i32 3))),
(PRMT_B32rrr $lo, $hi, $amt, PrmtF4E)>;
def byte_extract_prmt : ImmLeaf<i32, [{
return (Imm == 0x7770) || (Imm == 0x7771) || (Imm == 0x7772) || (Imm == 0x7773);
}]>;
def to_sign_extend_selector : SDNodeXForm<imm, [{
const APInt &V = N->getAPIntValue();
const APInt B = V.trunc(4);
const APInt BSext = B | 8;
const APInt R = BSext.concat(BSext).concat(BSext).concat(B).zext(32);
return CurDAG->getTargetConstant(R, SDLoc(N), MVT::i32);
}]>;
// byte extraction + signed/unsigned extension to i32.
def : Pat<(i32 (sext_inreg (prmt i32:$s, 0, byte_extract_prmt:$sel, PrmtNONE), i8)),
(PRMT_B32rii $s, 0, (to_sign_extend_selector $sel), PrmtNONE)>;
// byte extraction + signed extension to i16
def : Pat<(i16 (sext_inreg (trunc (prmt i32:$s, 0, byte_extract_prmt:$sel, PrmtNONE)), i8)),
(CVT_u16_u32 (PRMT_B32rii $s, 0, (to_sign_extend_selector $sel), PrmtNONE), CvtNONE)>;
// Byte extraction via shift/trunc/sext
def : Pat<(i16 (sext_inreg (trunc i32:$s), i8)), (CVT_s8_s32 $s, CvtNONE)>;
def : Pat<(i16 (sext_inreg (trunc i64:$s), i8)), (CVT_s8_s64 $s, CvtNONE)>;
def : Pat<(sext_inreg (srl i32:$s, (i32 imm:$o)), i8), (BFE_S32rii $s, imm:$o, 8)>;
def : Pat<(sext_inreg (srl i64:$s, (i32 imm:$o)), i8), (BFE_S64rii $s, imm:$o, 8)>;
def : Pat<(i16 (sext_inreg (trunc (srl i32:$s, (i32 imm:$o))), i8)),
(CVT_s8_s32 (BFE_S32rii $s, imm:$o, 8), CvtNONE)>;
def : Pat<(i16 (sext_inreg (trunc (srl i64:$s, (i32 imm:$o))), i8)),
(CVT_s8_s64 (BFE_S64rii $s, imm:$o, 8), CvtNONE)>;
def : Pat<(i16 (sra (i16 (trunc i32:$s)), (i32 8))),
(CVT_s8_s32 (BFE_S32rii $s, 8, 8), CvtNONE)>;
//-----------------------------------
// Comparison instructions (setp, set)
//-----------------------------------
// FIXME: This doesn't cover versions of set and setp that combine with a
// boolean predicate, e.g. setp.eq.and.b16.
def cond2cc : SDNodeXForm<cond, [{
return getPTXCmpMode(*N);
}]>;
multiclass FSETP<RegTyInfo t, bit allow_ftz = true> {
defvar ftz_str = !if(allow_ftz, "$ftz", "");
defvar op_str = "setp.${cmp:FCmp}" # ftz_str # "." # t.PtxType;
defvar flags = !con((ins CmpMode:$cmp), !if(allow_ftz, (ins FTZFlag:$ftz), (ins)));
let hasSideEffects = false, isCompare = 1, isCommutable = 1 in {
def rr :
BasicFlagsNVPTXInst<(outs B1:$dst), (ins t.RC:$a, t.RC:$b),
flags, op_str>;
if t.SupportsImm then {
def ri :
BasicFlagsNVPTXInst<(outs B1:$dst), (ins t.RC:$a, t.Imm:$b),
flags, op_str>;
def ir :
BasicFlagsNVPTXInst<(outs B1:$dst), (ins t.Imm:$a, t.RC:$b),
flags, op_str>;
}
}
def : Pat<(i1 (setcc t.Ty:$a, t.Ty:$b, cond:$cc)),
(!cast<NVPTXInst>(NAME # "rr") $a, $b, (cond2cc $cc))>;
if t.SupportsImm then {
def : Pat<(i1 (setcc t.Ty:$a, fpimm:$b, cond:$cc)),
(!cast<NVPTXInst>(NAME # "ri") $a, fpimm:$b, (cond2cc $cc))>;
def : Pat<(i1 (setcc fpimm:$a, t.Ty:$b, cond:$cc)),
(!cast<NVPTXInst>(NAME # "ir") fpimm:$a, $b, (cond2cc $cc))>;
}
}
multiclass ISETP<RegTyInfo t> {
defvar op_str = "setp.${cmp:ICmp}.${cmp:IType}" # t.Size;
let hasSideEffects = false, isCompare = 1, isCommutable = 1 in {
def rr :
BasicFlagsNVPTXInst<(outs B1:$dst), (ins t.RC:$a, t.RC:$b),
(ins CmpMode:$cmp), op_str>;
def ri :
BasicFlagsNVPTXInst<(outs B1:$dst), (ins t.RC:$a, t.Imm:$b),
(ins CmpMode:$cmp), op_str>;
def ir :
BasicFlagsNVPTXInst<(outs B1:$dst), (ins t.Imm:$a, t.RC:$b),
(ins CmpMode:$cmp), op_str>;
}
def : Pat<(i1 (setcc t.Ty:$a, t.Ty:$b, cond:$cc)),
(!cast<NVPTXInst>(NAME # "rr") $a, $b, (cond2cc $cc))>;
def : Pat<(i1 (setcc t.Ty:$a, imm:$b, cond:$cc)),
(!cast<NVPTXInst>(NAME # "ri") $a, imm:$b, (cond2cc $cc))>;
def : Pat<(i1 (setcc imm:$a, t.Ty:$b, cond:$cc)),
(!cast<NVPTXInst>(NAME # "ir") imm:$a, $b, (cond2cc $cc))>;
}
defm SETP_i16 : ISETP<I16RT>;
defm SETP_i32 : ISETP<I32RT>;
defm SETP_i64 : ISETP<I64RT>;
defm SETP_f32 : FSETP<F32RT>;
defm SETP_f64 : FSETP<F64RT, allow_ftz = false>;
let Predicates = [useFP16Math] in
defm SETP_f16 : FSETP<F16RT>;
let Predicates = [hasBF16Math, hasPTX<78>, hasSM<90>] in
defm SETP_bf16 : FSETP<BF16RT>;
def SETP_f16x2rr :
BasicFlagsNVPTXInst<(outs B1:$p, B1:$q),
(ins B32:$a, B32:$b), (ins CmpMode:$cmp, FTZFlag:$ftz),
"setp.${cmp:FCmp}$ftz.f16x2">,
Requires<[useFP16Math]>;
def SETP_bf16x2rr :
BasicFlagsNVPTXInst<(outs B1:$p, B1:$q),
(ins B32:$a, B32:$b), (ins CmpMode:$cmp, FTZFlag:$ftz),
"setp.${cmp:FCmp}$ftz.bf16x2">,
Requires<[hasBF16Math, hasPTX<78>, hasSM<90>]>;
//-----------------------------------
// Data Movement (Load / Store, Move)
//-----------------------------------
def addr : ComplexPattern<pAny, 2, "SelectADDR">;
def ADDR_base : Operand<pAny>;
def ADDR : Operand<pAny> {
let PrintMethod = "printMemOperand";
let MIOperandInfo = (ops ADDR_base, i32imm);
}
def UsedBytesMask : Operand<i32> {
let PrintMethod = "printUsedBytesMaskPragma";
}
def RegOrSink : Operand<Any> {
let PrintMethod = "printRegisterOrSinkSymbol";
}
def AtomicCode : Operand<i32> {
let PrintMethod = "printAtomicCode";
}
def MmaCode : Operand<i32> {
let PrintMethod = "printMmaCode";
}
// Get pointer to local stack.
let hasSideEffects = false in {
def MOV_DEPOT_ADDR : NVPTXInst<(outs B32:$d), (ins i32imm:$num),
"mov.b32 \t$d, __local_depot$num;">;
def MOV_DEPOT_ADDR_64 : NVPTXInst<(outs B64:$d), (ins i32imm:$num),
"mov.b64 \t$d, __local_depot$num;">;
}
let hasSideEffects = false in {
let isMoveReg = true, isAsCheapAsAMove = true in
class MOVr<RegisterClass RC, string OpStr> :
BasicNVPTXInst<(outs RC:$dst), (ins RC:$src), "mov." # OpStr>;
let isMoveImm = true, isAsCheapAsAMove = true in
class MOVi<RegTyInfo t, string suffix> :
BasicNVPTXInst<(outs t.RC:$dst), (ins t.Imm:$src),
"mov." # suffix,
[(set t.Ty:$dst, t.ImmNode:$src)]>;
// We don't want to set isAsCheapAsAMove to true for these instructions as
// this would prevent CSE and resulted in regressions (see discussion after
// PR-145581 in llvm-project).
class MovSymInst<RegTyInfo t> :
BasicNVPTXInst<(outs t.RC:$dst), (ins Operand<t.Ty>:$src),
"mov.b" # t.Size>;
}
def MOV_B1_r : MOVr<B1, "pred">;
def MOV_B16_r : MOVr<B16, "b16">;
def MOV_B32_r : MOVr<B32, "b32">;
def MOV_B64_r : MOVr<B64, "b64">;
def MOV_B128_r : MOVr<B128, "b128">;
def MOV_B1_i : MOVi<I1RT, "pred">;
def MOV_B16_i : MOVi<I16RT, "b16">;
def MOV_B32_i : MOVi<I32RT, "b32">;
def MOV_B64_i : MOVi<I64RT, "b64">;
def MOV_F16_i : MOVi<F16RT, "b16">;
def MOV_BF16_i : MOVi<BF16RT, "b16">;
def MOV_F32_i : MOVi<F32RT, "b32">;
def MOV_F64_i : MOVi<F64RT, "b64">;
def MOV_B32_sym : MovSymInst<I32RT>;
def MOV_B64_sym : MovSymInst<I64RT>;
def to_tglobaladdr : SDNodeXForm<globaladdr, [{
return CurDAG->getTargetGlobalAddress(N->getGlobal(), SDLoc(N),
N->getValueType(0), N->getOffset(),
N->getTargetFlags());
}]>;
def to_texternsym : SDNodeXForm<externalsym, [{
return CurDAG->getTargetExternalSymbol(N->getSymbol(), N->getValueType(0),
N->getTargetFlags());
}]>;
def to_tframeindex : SDNodeXForm<frameindex, [{
return CurDAG->getTargetFrameIndex(N->getIndex(), N->getValueType(0));
}]>;
def : Pat<(i32 globaladdr:$dst), (MOV_B32_sym (to_tglobaladdr $dst))>;
def : Pat<(i64 globaladdr:$dst), (MOV_B64_sym (to_tglobaladdr $dst))>;
def : Pat<(i32 externalsym:$dst), (MOV_B32_sym (to_texternsym $dst))>;
def : Pat<(i64 externalsym:$dst), (MOV_B64_sym (to_texternsym $dst))>;
//---- Copy Frame Index ----
def LEA_ADDRi : NVPTXInst<(outs B32:$dst), (ins ADDR:$addr),
"add.u32 \t$dst, ${addr:add};">;
def LEA_ADDRi64 : NVPTXInst<(outs B64:$dst), (ins ADDR:$addr),
"add.u64 \t$dst, ${addr:add};">;
def : Pat<(i32 frameindex:$fi), (LEA_ADDRi (to_tframeindex $fi), 0)>;
def : Pat<(i64 frameindex:$fi), (LEA_ADDRi64 (to_tframeindex $fi), 0)>;
//-----------------------------------
// Comparison and Selection
//-----------------------------------
// TODO: These patterns seem very specific and brittle. We should try to find
// a more general solution.
def cond_signed : PatLeaf<(cond), [{
return isSignedIntSetCC(N->get());
}]>;
// A 16-bit signed comparison of sign-extended byte extracts can be converted
// to 32-bit comparison if we change the PRMT to sign-extend the extracted
// bytes.
def : Pat<(setcc (i16 (sext_inreg (trunc (prmt i32:$a, 0, byte_extract_prmt:$sel_a, PrmtNONE)), i8)),
(i16 (sext_inreg (trunc (prmt i32:$b, 0, byte_extract_prmt:$sel_b, PrmtNONE)), i8)),
cond_signed:$cc),
(SETP_i32rr (PRMT_B32rii i32:$a, 0, (to_sign_extend_selector $sel_a), PrmtNONE),
(PRMT_B32rii i32:$b, 0, (to_sign_extend_selector $sel_b), PrmtNONE),
(cond2cc $cc))>;
def SDTDeclareArrayParam :
SDTypeProfile<0, 3, [SDTCisVT<0, i32>, SDTCisVT<1, i32>, SDTCisVT<2, i32>]>;
def SDTDeclareScalarParam :
SDTypeProfile<0, 2, [SDTCisVT<0, i32>, SDTCisVT<1, i32>]>;
def SDTMoveParamProfile : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisSameAs<0, 1>]>;
def SDTProxyReg : SDTypeProfile<1, 1, [SDTCisSameAs<0, 1>]>;
// These nodes represent a parameter declaration. In PTX this will look like:
// .param .align 16 .b8 param0[1024];
// .param .b32 retval0;
//
// DeclareArrayParam(Chain, Externalsym, Align, Size, Glue)
// DeclareScalarParam(Chain, Externalsym, Size, Glue)
def declare_array_param :
SDNode<"NVPTXISD::DeclareArrayParam", SDTDeclareArrayParam,
[SDNPHasChain, SDNPOutGlue, SDNPInGlue, SDNPSideEffect]>;
def declare_scalar_param :
SDNode<"NVPTXISD::DeclareScalarParam", SDTDeclareScalarParam,
[SDNPHasChain, SDNPOutGlue, SDNPInGlue, SDNPSideEffect]>;
def MoveParam :
SDNode<"NVPTXISD::MoveParam", SDTMoveParamProfile, []>;
def proxy_reg :
SDNode<"NVPTXISD::ProxyReg", SDTProxyReg, [SDNPHasChain]>;
/// CALL(Chain, IsConvergent, IsIndirectCall/IsUniform, NumReturns,
/// NumParams, Callee, Proto)
def SDTCallProfile : SDTypeProfile<0, 6,
[SDTCisVT<0, i32>, SDTCisVT<1, i32>, SDTCisVT<2, i32>,
SDTCisVT<3, i32>, SDTCisVT<5, i32>]>;
def call : SDNode<"NVPTXISD::CALL", SDTCallProfile, [SDNPHasChain, SDNPSideEffect]>;
/// CALL(Chain, IsConvergent, IsIndirectCall/IsUniform, NumReturns,
/// NumParams, Callee, Proto)
def CallOperand : Operand<i32> { let PrintMethod = "printCallOperand"; }
foreach is_convergent = [0, 1] in {
defvar convergent_suffix = !if(is_convergent, "_conv", "");
let isCall = 1, isConvergent = is_convergent in {
def CALL # convergent_suffix :
NVPTXInst<(outs),
(ins ADDR_base:$addr, CallOperand:$rets, CallOperand:$params,
i32imm:$proto),
"call${rets:RetList} $addr, (${params:ParamList}), prototype_$proto;">;
def CALL_UNI # convergent_suffix :
NVPTXInst<(outs),
(ins ADDR_base:$addr, CallOperand:$rets, CallOperand:$params),
"call.uni${rets:RetList} $addr, (${params:ParamList});">;
}
defvar call_inst = !cast<NVPTXInst>("CALL" # convergent_suffix);
def : Pat<(call is_convergent, 1, imm:$rets, imm:$params, i32:$addr, imm:$proto),
(call_inst $addr, imm:$rets, imm:$params, imm:$proto)>;
def : Pat<(call is_convergent, 1, imm:$rets, imm:$params, i64:$addr, imm:$proto),
(call_inst $addr, imm:$rets, imm:$params, imm:$proto)>;
defvar call_uni_inst = !cast<NVPTXInst>("CALL_UNI" # convergent_suffix);
def : Pat<(call is_convergent, 0, imm:$rets, imm:$params, globaladdr:$addr, 0),
(call_uni_inst (to_tglobaladdr $addr), imm:$rets, imm:$params)>;
}
def DECLARE_PARAM_array :
NVPTXInst<(outs), (ins i32imm:$a, i32imm:$align, i32imm:$size),
".param .align $align .b8 \t$a[$size];">;
def DECLARE_PARAM_scalar :
NVPTXInst<(outs), (ins i32imm:$a, i32imm:$size),
".param .b$size \t$a;">;
def : Pat<(declare_array_param externalsym:$a, imm:$align, imm:$size),
(DECLARE_PARAM_array (to_texternsym $a), imm:$align, imm:$size)>;
def : Pat<(declare_scalar_param externalsym:$a, imm:$size),
(DECLARE_PARAM_scalar (to_texternsym $a), imm:$size)>;
// Call prototype wrapper, this is a dummy instruction that just prints it's
// operand which is string defining the prototype.
def SDTCallPrototype : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
def CallPrototype :
SDNode<"NVPTXISD::CallPrototype", SDTCallPrototype,
[SDNPHasChain, SDNPSideEffect]>;
def ProtoIdent : Operand<i32> { let PrintMethod = "printProtoIdent"; }
def CALL_PROTOTYPE :
NVPTXInst<(outs), (ins ProtoIdent:$ident),
"$ident", [(CallPrototype (i32 texternalsym:$ident))]>;
foreach t = [I32RT, I64RT] in {
defvar inst_name = "MOV" # t.Size # "_PARAM";
def inst_name : BasicNVPTXInst<(outs t.RC:$dst), (ins t.RC:$src), "mov.b" # t.Size>;
def : Pat<(MoveParam (t.Ty externalsym:$src)),
(!cast<NVPTXInst>(inst_name) (t.Ty (to_texternsym $src)))>;
}
multiclass ProxyRegInst<string SzStr, NVPTXRegClass rc> {
def NAME : BasicNVPTXInst<(outs rc:$dst), (ins rc:$src),
"mov." # SzStr>;
foreach vt = rc.RegTypes in
def : Pat<(vt (proxy_reg vt:$src)), (!cast<NVPTXInst>(NAME) $src)>;
}
defm ProxyRegB1 : ProxyRegInst<"pred", B1>;
defm ProxyRegB16 : ProxyRegInst<"b16", B16>;
defm ProxyRegB32 : ProxyRegInst<"b32", B32>;
defm ProxyRegB64 : ProxyRegInst<"b64", B64>;
// Callseq start and end
// Note: these nodes are marked as SDNPMayStore and SDNPMayLoad because
// they define the scope in which the declared params may be used. Therefore
// we add these flags to ensure ld.param and st.param are not sunk or hoisted
// out of that scope.
def callseq_start : SDNode<"ISD::CALLSEQ_START",
SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>,
[SDNPHasChain, SDNPOutGlue,
SDNPSideEffect, SDNPMayStore, SDNPMayLoad]>;
def callseq_end : SDNode<"ISD::CALLSEQ_END",
SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>,
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
SDNPSideEffect, SDNPMayStore, SDNPMayLoad]>;
def Callseq_Start :
NVPTXInst<(outs), (ins i32imm:$amt1, i32imm:$amt2),
"\\{ // callseq $amt1, $amt2",
[(callseq_start timm:$amt1, timm:$amt2)]>;
def Callseq_End :
NVPTXInst<(outs), (ins i32imm:$amt1, i32imm:$amt2),
"\\} // callseq $amt1",
[(callseq_end timm:$amt1, timm:$amt2)]>;
//
// Load / Store Handling
//
class LD<NVPTXRegClass regclass>
: NVPTXInst<
(outs regclass:$dst),
(ins AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp,
AtomicCode:$Sign, i32imm:$fromWidth, UsedBytesMask:$usedBytes,
ADDR:$addr),
"${usedBytes}"
"ld${sem:sem}${scope:scope}${addsp:addsp}.${Sign:sign}$fromWidth "
"\t$dst, [$addr];">;
let mayLoad=1, hasSideEffects=0 in {
def LD_i16 : LD<B16>;
def LD_i32 : LD<B32>;
def LD_i64 : LD<B64>;
}
class ST<DAGOperand O>
: NVPTXInst<
(outs),
(ins O:$src,
AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp, i32imm:$toWidth,
ADDR:$addr),
"st${sem:sem}${scope:scope}${addsp:addsp}.b$toWidth"
" \t[$addr], $src;">;
let mayStore=1, hasSideEffects=0 in {
def ST_i16 : ST<RI16>;
def ST_i32 : ST<RI32>;
def ST_i64 : ST<RI64>;
}
// The following is used only in and after vector elementizations. Vector
// elementization happens at the machine instruction level, so the following
// instructions never appear in the DAG.
multiclass LD_VEC<NVPTXRegClass regclass, bit support_v8 = false> {
def _v2 : NVPTXInst<
(outs regclass:$dst1, regclass:$dst2),
(ins AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp,
AtomicCode:$Sign, i32imm:$fromWidth, UsedBytesMask:$usedBytes,
ADDR:$addr),
"${usedBytes}"
"ld${sem:sem}${scope:scope}${addsp:addsp}.v2.${Sign:sign}$fromWidth "
"\t{{$dst1, $dst2}}, [$addr];">;
def _v4 : NVPTXInst<
(outs regclass:$dst1, regclass:$dst2, regclass:$dst3, regclass:$dst4),
(ins AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp,
AtomicCode:$Sign, i32imm:$fromWidth, UsedBytesMask:$usedBytes,
ADDR:$addr),
"${usedBytes}"
"ld${sem:sem}${scope:scope}${addsp:addsp}.v4.${Sign:sign}$fromWidth "
"\t{{$dst1, $dst2, $dst3, $dst4}}, [$addr];">;
if support_v8 then
def _v8 : NVPTXInst<
(outs regclass:$dst1, regclass:$dst2, regclass:$dst3, regclass:$dst4,
regclass:$dst5, regclass:$dst6, regclass:$dst7, regclass:$dst8),
(ins AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp,
AtomicCode:$Sign, i32imm:$fromWidth, UsedBytesMask:$usedBytes,
ADDR:$addr),
"${usedBytes}"
"ld${sem:sem}${scope:scope}${addsp:addsp}.v8.${Sign:sign}$fromWidth "
"\t{{$dst1, $dst2, $dst3, $dst4, $dst5, $dst6, $dst7, $dst8}}, "
"[$addr];">;
}
let mayLoad=1, hasSideEffects=0 in {
defm LDV_i16 : LD_VEC<B16>;
defm LDV_i32 : LD_VEC<B32, support_v8 = true>;
defm LDV_i64 : LD_VEC<B64>;
}
multiclass ST_VEC<DAGOperand O, bit support_v8 = false> {
def _v2 : NVPTXInst<
(outs),
(ins O:$src1, O:$src2,
AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp, i32imm:$fromWidth,
ADDR:$addr),
"st${sem:sem}${scope:scope}${addsp:addsp}.v2.b$fromWidth "
"\t[$addr], {{$src1, $src2}};">;
def _v4 : NVPTXInst<
(outs),
(ins RegOrSink:$src1, RegOrSink:$src2, RegOrSink:$src3, RegOrSink:$src4,
AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp, i32imm:$fromWidth,
ADDR:$addr),
"st${sem:sem}${scope:scope}${addsp:addsp}.v4.b$fromWidth "
"\t[$addr], {{$src1, $src2, $src3, $src4}};">;
if support_v8 then
def _v8 : NVPTXInst<
(outs),
(ins RegOrSink:$src1, RegOrSink:$src2, RegOrSink:$src3, RegOrSink:$src4,
RegOrSink:$src5, RegOrSink:$src6, RegOrSink:$src7, RegOrSink:$src8,
AtomicCode:$sem, AtomicCode:$scope, AtomicCode:$addsp, i32imm:$fromWidth,
ADDR:$addr),
"st${sem:sem}${scope:scope}${addsp:addsp}.v8.b$fromWidth "
"\t[$addr], "
"{{$src1, $src2, $src3, $src4, $src5, $src6, $src7, $src8}};">;
}
let mayStore=1, hasSideEffects=0 in {
defm STV_i16 : ST_VEC<RI16>;
defm STV_i32 : ST_VEC<RI32, support_v8 = true>;
defm STV_i64 : ST_VEC<RI64>;
}
//---- Conversion ----
foreach rc = [B16, B32, B64] in
foreach ta = rc.RegTypes in
foreach tb = rc.RegTypes in
if !ne(ta, tb) then
def : Pat<(ta (bitconvert tb:$a)),
(ta rc:$a)>;
// NOTE: pred->fp are currently sub-optimal due to an issue in TableGen where
// we cannot specify floating-point literals in isel patterns. Therefore, we
// use an integer selp to select either 1 (or -1 in case of signed) or 0
// and then cvt to floating-point.
// sint -> f16
def : Pat<(f16 (sint_to_fp i1:$a)), (CVT_f16_s32 (SELP_b32ii -1, 0, $a), CvtRN)>;
def : Pat<(f16 (sint_to_fp i16:$a)), (CVT_f16_s16 $a, CvtRN)>;
def : Pat<(f16 (sint_to_fp i32:$a)), (CVT_f16_s32 $a, CvtRN)>;
def : Pat<(f16 (sint_to_fp i64:$a)), (CVT_f16_s64 $a, CvtRN)>;
// uint -> f16
def : Pat<(f16 (uint_to_fp i1:$a)), (CVT_f16_u32 (SELP_b32ii 1, 0, $a), CvtRN)>;
def : Pat<(f16 (uint_to_fp i16:$a)), (CVT_f16_u16 $a, CvtRN)>;
def : Pat<(f16 (uint_to_fp i32:$a)), (CVT_f16_u32 $a, CvtRN)>;
def : Pat<(f16 (uint_to_fp i64:$a)), (CVT_f16_u64 $a, CvtRN)>;
// sint -> bf16
let Predicates = [hasPTX<78>, hasSM<90>] in {
def : Pat<(bf16 (sint_to_fp i1:$a)), (CVT_bf16_s32 (SELP_b32ii -1, 0, $a), CvtRN)>;
def : Pat<(bf16 (sint_to_fp i16:$a)), (CVT_bf16_s16 $a, CvtRN)>;
def : Pat<(bf16 (sint_to_fp i32:$a)), (CVT_bf16_s32 $a, CvtRN)>;
def : Pat<(bf16 (sint_to_fp i64:$a)), (CVT_bf16_s64 $a, CvtRN)>;
}
// uint -> bf16
let Predicates = [hasPTX<78>, hasSM<90>] in {
def : Pat<(bf16 (uint_to_fp i1:$a)), (CVT_bf16_u32 (SELP_b32ii 1, 0, $a), CvtRN)>;
def : Pat<(bf16 (uint_to_fp i16:$a)), (CVT_bf16_u16 $a, CvtRN)>;
def : Pat<(bf16 (uint_to_fp i32:$a)), (CVT_bf16_u32 $a, CvtRN)>;
def : Pat<(bf16 (uint_to_fp i64:$a)), (CVT_bf16_u64 $a, CvtRN)>;
}
// sint -> f32
def : Pat<(f32 (sint_to_fp i1:$a)), (CVT_f32_s32 (SELP_b32ii -1, 0, $a), CvtRN)>;
def : Pat<(f32 (sint_to_fp i16:$a)), (CVT_f32_s16 $a, CvtRN)>;
def : Pat<(f32 (sint_to_fp i32:$a)), (CVT_f32_s32 $a, CvtRN)>;
def : Pat<(f32 (sint_to_fp i64:$a)), (CVT_f32_s64 $a, CvtRN)>;
// uint -> f32
def : Pat<(f32 (uint_to_fp i1:$a)), (CVT_f32_u32 (SELP_b32ii 1, 0, $a), CvtRN)>;
def : Pat<(f32 (uint_to_fp i16:$a)), (CVT_f32_u16 $a, CvtRN)>;
def : Pat<(f32 (uint_to_fp i32:$a)), (CVT_f32_u32 $a, CvtRN)>;
def : Pat<(f32 (uint_to_fp i64:$a)), (CVT_f32_u64 $a, CvtRN)>;
// sint -> f64
def : Pat<(f64 (sint_to_fp i1:$a)), (CVT_f64_s32 (SELP_b32ii -1, 0, $a), CvtRN)>;
def : Pat<(f64 (sint_to_fp i16:$a)), (CVT_f64_s16 $a, CvtRN)>;
def : Pat<(f64 (sint_to_fp i32:$a)), (CVT_f64_s32 $a, CvtRN)>;
def : Pat<(f64 (sint_to_fp i64:$a)), (CVT_f64_s64 $a, CvtRN)>;
// uint -> f64
def : Pat<(f64 (uint_to_fp i1:$a)), (CVT_f64_u32 (SELP_b32ii 1, 0, $a), CvtRN)>;
def : Pat<(f64 (uint_to_fp i16:$a)), (CVT_f64_u16 $a, CvtRN)>;
def : Pat<(f64 (uint_to_fp i32:$a)), (CVT_f64_u32 $a, CvtRN)>;
def : Pat<(f64 (uint_to_fp i64:$a)), (CVT_f64_u64 $a, CvtRN)>;
// fp_to_sint/uint to i1 is custom-lowered to an fp compare
// (NVPTXTargetLowering::LowerOperation), so no i1 patterns appear below.
// f16 -> sint
def : Pat<(i16 (fp_to_sint f16:$a)), (CVT_s16_f16 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_sint f16:$a)), (CVT_s32_f16 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_sint f16:$a)), (CVT_s64_f16 $a, CvtRZI)>;
// f16 -> uint
def : Pat<(i16 (fp_to_uint f16:$a)), (CVT_u16_f16 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_uint f16:$a)), (CVT_u32_f16 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_uint f16:$a)), (CVT_u64_f16 $a, CvtRZI)>;
// bf16 -> sint
def : Pat<(i16 (fp_to_sint bf16:$a)), (CVT_s16_bf16 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_sint bf16:$a)), (CVT_s32_bf16 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_sint bf16:$a)), (CVT_s64_bf16 $a, CvtRZI)>;
// bf16 -> uint
def : Pat<(i16 (fp_to_uint bf16:$a)), (CVT_u16_bf16 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_uint bf16:$a)), (CVT_u32_bf16 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_uint bf16:$a)), (CVT_u64_bf16 $a, CvtRZI)>;
// f32 -> sint
let Predicates = [doF32FTZ] in {
def : Pat<(i16 (fp_to_sint f32:$a)), (CVT_s16_f32 $a, CvtRZI_FTZ)>;
def : Pat<(i32 (fp_to_sint f32:$a)), (CVT_s32_f32 $a, CvtRZI_FTZ)>;
def : Pat<(i64 (fp_to_sint f32:$a)), (CVT_s64_f32 $a, CvtRZI_FTZ)>;
}
def : Pat<(i16 (fp_to_sint f32:$a)), (CVT_s16_f32 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_sint f32:$a)), (CVT_s32_f32 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_sint f32:$a)), (CVT_s64_f32 $a, CvtRZI)>;
// f32 -> uint
let Predicates = [doF32FTZ] in {
def : Pat<(i16 (fp_to_uint f32:$a)), (CVT_u16_f32 $a, CvtRZI_FTZ)>;
def : Pat<(i32 (fp_to_uint f32:$a)), (CVT_u32_f32 $a, CvtRZI_FTZ)>;
def : Pat<(i64 (fp_to_uint f32:$a)), (CVT_u64_f32 $a, CvtRZI_FTZ)>;
}
def : Pat<(i16 (fp_to_uint f32:$a)), (CVT_u16_f32 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_uint f32:$a)), (CVT_u32_f32 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_uint f32:$a)), (CVT_u64_f32 $a, CvtRZI)>;
// f64 -> sint
def : Pat<(i16 (fp_to_sint f64:$a)), (CVT_s16_f64 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_sint f64:$a)), (CVT_s32_f64 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_sint f64:$a)), (CVT_s64_f64 $a, CvtRZI)>;
// f64 -> uint
def : Pat<(i16 (fp_to_uint f64:$a)), (CVT_u16_f64 $a, CvtRZI)>;
def : Pat<(i32 (fp_to_uint f64:$a)), (CVT_u32_f64 $a, CvtRZI)>;
def : Pat<(i64 (fp_to_uint f64:$a)), (CVT_u64_f64 $a, CvtRZI)>;
// sext i1
def : Pat<(i16 (sext i1:$a)), (SELP_b16ii -1, 0, $a)>;
def : Pat<(i32 (sext i1:$a)), (SELP_b32ii -1, 0, $a)>;
def : Pat<(i64 (sext i1:$a)), (SELP_b64ii -1, 0, $a)>;
// zext i1
def : Pat<(i16 (zext i1:$a)), (SELP_b16ii 1, 0, $a)>;
def : Pat<(i32 (zext i1:$a)), (SELP_b32ii 1, 0, $a)>;
def : Pat<(i64 (zext i1:$a)), (SELP_b64ii 1, 0, $a)>;
// anyext i1
def : Pat<(i16 (anyext i1:$a)), (SELP_b16ii -1, 0, $a)>;
def : Pat<(i32 (anyext i1:$a)), (SELP_b32ii -1, 0, $a)>;
def : Pat<(i64 (anyext i1:$a)), (SELP_b64ii -1, 0, $a)>;
// sext i16
def : Pat<(i32 (sext i16:$a)), (CVT_s32_s16 $a, CvtNONE)>;
def : Pat<(i64 (sext i16:$a)), (CVT_s64_s16 $a, CvtNONE)>;
// zext i16
def : Pat<(i32 (zext i16:$a)), (CVT_u32_u16 $a, CvtNONE)>;
def : Pat<(i64 (zext i16:$a)), (CVT_u64_u16 $a, CvtNONE)>;
// anyext i16
def : Pat<(i32 (anyext i16:$a)), (CVT_u32_u16 $a, CvtNONE)>;
def : Pat<(i64 (anyext i16:$a)), (CVT_u64_u16 $a, CvtNONE)>;
// sext i32
def : Pat<(i64 (sext i32:$a)), (CVT_s64_s32 $a, CvtNONE)>;
// zext i32
def : Pat<(i64 (zext i32:$a)), (CVT_u64_u32 $a, CvtNONE)>;
// anyext i32
def : Pat<(i64 (anyext i32:$a)), (CVT_u64_u32 $a, CvtNONE)>;
// truncate i64
def : Pat<(i32 (trunc i64:$a)), (CVT_u32_u64 $a, CvtNONE)>;
def : Pat<(i16 (trunc i64:$a)), (CVT_u16_u64 $a, CvtNONE)>;
def : Pat<(i1 (trunc i64:$a)), (SETP_i64ri (AND_b64ri $a, 1), 0, CmpNE)>;
// truncate i32
def : Pat<(i16 (trunc i32:$a)), (CVT_u16_u32 $a, CvtNONE)>;
def : Pat<(i1 (trunc i32:$a)), (SETP_i32ri (AND_b32ri $a, 1), 0, CmpNE)>;
// truncate i16
def : Pat<(i1 (trunc i16:$a)), (SETP_i16ri (AND_b16ri $a, 1), 0, CmpNE)>;
// sext_inreg
def : Pat<(sext_inreg i16:$a, i8), (CVT_INREG_s16_s8 $a)>;
def : Pat<(sext_inreg i32:$a, i8), (CVT_INREG_s32_s8 $a)>;
def : Pat<(sext_inreg i32:$a, i16), (CVT_INREG_s32_s16 $a)>;
def : Pat<(sext_inreg i64:$a, i8), (CVT_INREG_s64_s8 $a)>;
def : Pat<(sext_inreg i64:$a, i16), (CVT_INREG_s64_s16 $a)>;
def : Pat<(sext_inreg i64:$a, i32), (CVT_INREG_s64_s32 $a)>;
let hasSideEffects = false in {
// pack a set of smaller int registers to a larger int register
def V4I16toI64 : NVPTXInst<(outs B64:$d),
(ins B16:$s1, B16:$s2,
B16:$s3, B16:$s4),
"mov.b64 \t$d, {{$s1, $s2, $s3, $s4}};">;
def V2I16toI32 : NVPTXInst<(outs B32:$d),
(ins B16:$s1, B16:$s2),
"mov.b32 \t$d, {{$s1, $s2}};">;
def V2I32toI64 : NVPTXInst<(outs B64:$d),
(ins B32:$s1, B32:$s2),
"mov.b64 \t$d, {{$s1, $s2}};">;
def V2I64toI128 : NVPTXInst<(outs B128:$d),
(ins B64:$s1, B64:$s2),
"mov.b128 \t$d, {{$s1, $s2}};">;
// unpack a larger int register to a set of smaller int registers
def I64toV4I16 : NVPTXInst<(outs B16:$d1, B16:$d2,
B16:$d3, B16:$d4),
(ins B64:$s),
"mov.b64 \t{{$d1, $d2, $d3, $d4}}, $s;">;
def I32toV2I16 : NVPTXInst<(outs B16:$d1, B16:$d2),
(ins B32:$s),
"mov.b32 \t{{$d1, $d2}}, $s;">;
def I64toV2I32 : NVPTXInst<(outs B32:$d1, B32:$d2),
(ins B64:$s),
"mov.b64 \t{{$d1, $d2}}, $s;">;
def I128toV2I64: NVPTXInst<(outs B64:$d1, B64:$d2),
(ins B128:$s),
"mov.b128 \t{{$d1, $d2}}, $s;">;
def I32toI16H : NVPTXInst<(outs B16:$high), (ins B32:$s),
"{{ .reg .b16 tmp; mov.b32 {tmp, $high}, $s; }}">;
def I32toI16L : NVPTXInst<(outs B16:$low), (ins B32:$s),
"{{ .reg .b16 tmp; mov.b32 {$low, tmp}, $s; }}">;
def I64toI32H : NVPTXInst<(outs B32:$high), (ins B64:$s),
"{{ .reg .b32 tmp; mov.b64 {tmp, $high}, $s; }}">;
def I64toI32L : NVPTXInst<(outs B32:$low), (ins B64:$s),
"{{ .reg .b32 tmp; mov.b64 {$low, tmp}, $s; }}">;
// PTX 7.1 lets you avoid a temp register and just use _ as a "sink" for the
// unused high/low part.
let Predicates = [hasPTX<71>] in {
def I32toI16H_Sink : NVPTXInst<(outs B16:$high), (ins B32:$s),
"mov.b32 \t{{_, $high}}, $s;">;
def I32toI16L_Sink : NVPTXInst<(outs B16:$low), (ins B32:$s),
"mov.b32 \t{{$low, _}}, $s;">;
def I64toI32H_Sink : NVPTXInst<(outs B32:$high), (ins B64:$s),
"mov.b64 \t{{_, $high}}, $s;">;
def I64toI32L_Sink : NVPTXInst<(outs B32:$low), (ins B64:$s),
"mov.b64 \t{{$low, _}}, $s;">;
}
}
let Predicates = [hasPTX<71>] in {
def : Pat<(i16 (trunc (srl i32:$s, (i32 16)))), (I32toI16H_Sink i32:$s)>;
def : Pat<(i16 (trunc (sra i32:$s, (i32 16)))), (I32toI16H_Sink i32:$s)>;
def : Pat<(i32 (trunc (srl i64:$s, (i32 32)))), (I64toI32H_Sink i64:$s)>;
def : Pat<(i32 (trunc (sra i64:$s, (i32 32)))), (I64toI32H_Sink i64:$s)>;
}
// Fall back to the old way if we don't have PTX 7.1.
def : Pat<(i16 (trunc (srl i32:$s, (i32 16)))), (I32toI16H $s)>;
def : Pat<(i16 (trunc (sra i32:$s, (i32 16)))), (I32toI16H $s)>;
def : Pat<(i32 (trunc (srl i64:$s, (i32 32)))), (I64toI32H $s)>;
def : Pat<(i32 (trunc (sra i64:$s, (i32 32)))), (I64toI32H $s)>;
def: Pat<(i32 (sext (extractelt v2i16:$src, 0))),
(CVT_INREG_s32_s16 $src)>;
// Handle extracting one element from the pair (32-bit types)
foreach vt = [v2f16, v2bf16, v2i16] in {
def : Pat<(extractelt vt:$src, 0), (I32toI16L_Sink $src)>, Requires<[hasPTX<71>]>;
def : Pat<(extractelt vt:$src, 1), (I32toI16H_Sink $src)>, Requires<[hasPTX<71>]>;
def : Pat<(extractelt vt:$src, 0), (I32toI16L $src)>;
def : Pat<(extractelt vt:$src, 1), (I32toI16H $src)>;
def : Pat<(vt (build_vector vt.ElementType:$a, vt.ElementType:$b)),
(V2I16toI32 $a, $b)>;
}
// Handle extracting one element from the pair (64-bit types)
foreach vt = [v2f32, v2i32] in {
def : Pat<(extractelt vt:$src, 0), (I64toI32L_Sink $src)>, Requires<[hasPTX<71>]>;
def : Pat<(extractelt vt:$src, 1), (I64toI32H_Sink $src)>, Requires<[hasPTX<71>]>;
def : Pat<(extractelt vt:$src, 0), (I64toI32L $src)>;
def : Pat<(extractelt vt:$src, 1), (I64toI32H $src)>;
def : Pat<(vt (build_vector vt.ElementType:$a, vt.ElementType:$b)),
(V2I32toI64 $a, $b)>;
}
def: Pat<(v2i16 (scalar_to_vector i16:$a)),
(CVT_u32_u16 $a, CvtNONE)>;
def: Pat<(v2i32 (scalar_to_vector i32:$a)),
(CVT_u64_u32 $a, CvtNONE)>;
def: Pat<(v2f16 (scalar_to_vector f16:$a)),
(V2I16toI32 $a, 0)>;
def: Pat<(v2f32 (scalar_to_vector f32:$a)),
(V2I32toI64 $a, (i32 0))>;
// This node is similar to ISD::BUILD_VECTOR except that the output may be
// implicitly bitcast to a scalar. This allows for the representation of
// packing move instructions for vector types which are not legal i.e. v2i32
def nvptx_build_vector : SDNode<"NVPTXISD::BUILD_VECTOR", SDTypeProfile<1, 2, []>, []>;
def : Pat<(i64 (nvptx_build_vector i32:$a, i32:$b)),
(V2I32toI64 $a, $b)>;
//
// Funnel-Shift
//
// Create SDNodes so they can be used in the DAG code, e.g.
// NVPTXISelLowering (LowerShiftLeftParts and LowerShiftRightParts)
def fshl_clamp : SDNode<"NVPTXISD::FSHL_CLAMP", SDTIntShiftDOp, []>;
def fshr_clamp : SDNode<"NVPTXISD::FSHR_CLAMP", SDTIntShiftDOp, []>;
// Funnel shift, requires >= sm_32. Does not trap if amt is out of range, so
// no side effects.
let hasSideEffects = false in {
multiclass ShfInst<string mode, SDNode op> {
def _i
: BasicNVPTXInst<(outs B32:$dst),
(ins B32:$lo, B32:$hi, i32imm:$amt),
"shf." # mode # ".b32",
[(set i32:$dst,
(op i32:$hi, i32:$lo, (i32 imm:$amt)))]>,
Requires<[hasHWROT32]>;
def _r
: BasicNVPTXInst<(outs B32:$dst),
(ins B32:$lo, B32:$hi, B32:$amt),
"shf." # mode # ".b32",
[(set i32:$dst,
(op i32:$hi, i32:$lo, i32:$amt))]>,
Requires<[hasHWROT32]>;
}
defm SHF_L_CLAMP : ShfInst<"l.clamp", fshl_clamp>;
defm SHF_R_CLAMP : ShfInst<"r.clamp", fshr_clamp>;
defm SHF_L_WRAP : ShfInst<"l.wrap", fshl>;
defm SHF_R_WRAP : ShfInst<"r.wrap", fshr>;
}
def : Pat<(i32 (int_nvvm_fshl_clamp i32:$hi, i32:$lo, i32:$amt)),
(SHF_L_CLAMP_r $lo, $hi, $amt)>;
def : Pat<(i32 (int_nvvm_fshl_clamp i32:$hi, i32:$lo, (i32 imm:$amt))),
(SHF_L_CLAMP_i $lo, $hi, imm:$amt)>;
def : Pat<(i32 (int_nvvm_fshr_clamp i32:$hi, i32:$lo, i32:$amt)),
(SHF_R_CLAMP_r $lo, $hi, $amt)>;
def : Pat<(i32 (int_nvvm_fshr_clamp i32:$hi, i32:$lo, (i32 imm:$amt))),
(SHF_R_CLAMP_i $lo, $hi, imm:$amt)>;
let hasSideEffects = false in {
foreach RT = [I32RT, I64RT] in {
// Count leading zeros
def CLZr # RT.Size : BasicNVPTXInst<(outs B32:$d), (ins RT.RC:$a),
"clz.b" # RT.Size,
[(set i32:$d, (ctlz RT.Ty:$a))]>;
// Population count
def POPCr # RT.Size : BasicNVPTXInst<(outs B32:$d), (ins RT.RC:$a),
"popc.b" # RT.Size,
[(set i32:$d, (ctpop RT.Ty:$a))]>;
}
}
// fpround f32 -> f16
def : Pat<(f16 (fpround f32:$a)), (CVT_f16_f32 $a, CvtRN)>;
// fpround f32 -> bf16
def : Pat<(bf16 (fpround f32:$a)), (CVT_bf16_f32 $a, CvtRN)>,
Requires<[hasPTX<70>, hasSM<80>]>;
// fpround f64 -> f16
def : Pat<(f16 (fpround f64:$a)), (CVT_f16_f64 $a, CvtRN)>;
// fpround f64 -> bf16
def : Pat<(bf16 (fpround f64:$a)), (CVT_bf16_f64 $a, CvtRN)>,
Requires<[hasPTX<78>, hasSM<90>]>;
// fpround f64 -> f32
def : Pat<(f32 (fpround f64:$a)), (CVT_f32_f64 $a, CvtRN_FTZ)>, Requires<[doF32FTZ]>;
def : Pat<(f32 (fpround f64:$a)), (CVT_f32_f64 $a, CvtRN)>;
// fpextend f16 -> f32
def : Pat<(f32 (fpextend f16:$a)), (CVT_f32_f16 $a, CvtNONE_FTZ)>, Requires<[doF32FTZ]>;
def : Pat<(f32 (fpextend f16:$a)), (CVT_f32_f16 $a, CvtNONE)>;
// fpextend bf16 -> f32
def : Pat<(f32 (fpextend bf16:$a)), (CVT_f32_bf16 $a, CvtNONE_FTZ)>, Requires<[doF32FTZ, hasPTX<78>, hasSM<90>]>;
def : Pat<(f32 (fpextend bf16:$a)), (CVT_f32_bf16 $a, CvtNONE)>, Requires<[hasPTX<71>, hasSM<80>]>;
// fpextend f16 -> f64
def : Pat<(f64 (fpextend f16:$a)), (CVT_f64_f16 $a, CvtNONE)>;
// fpextend bf16 -> f64
def : Pat<(f64 (fpextend bf16:$a)), (CVT_f64_bf16 $a, CvtNONE)>, Requires<[hasPTX<78>, hasSM<90>]>;
// fpextend f32 -> f64
def : Pat<(f64 (fpextend f32:$a)), (CVT_f64_f32 $a, CvtNONE_FTZ)>, Requires<[doF32FTZ]>;
def : Pat<(f64 (fpextend f32:$a)), (CVT_f64_f32 $a, CvtNONE)>;
def retglue : SDNode<"NVPTXISD::RET_GLUE", SDTNone,
[SDNPHasChain, SDNPOptInGlue]>;
// fceil, ffloor, froundeven, ftrunc.
multiclass CVT_ROUND<SDNode OpNode, PatLeaf Mode, PatLeaf ModeFTZ> {
def : Pat<(OpNode f16:$a), (CVT_f16_f16 $a, Mode)>;
def : Pat<(OpNode bf16:$a), (CVT_bf16_bf16 $a, Mode)>;
def : Pat<(OpNode f32:$a), (CVT_f32_f32 $a, ModeFTZ)>, Requires<[doF32FTZ]>;
def : Pat<(OpNode f32:$a), (CVT_f32_f32 $a, Mode)>, Requires<[doNoF32FTZ]>;
def : Pat<(OpNode f64:$a), (CVT_f64_f64 $a, Mode)>;
}
defm : CVT_ROUND<fceil, CvtRPI, CvtRPI_FTZ>;
defm : CVT_ROUND<ffloor, CvtRMI, CvtRMI_FTZ>;
defm : CVT_ROUND<froundeven, CvtRNI, CvtRNI_FTZ>;
defm : CVT_ROUND<ftrunc, CvtRZI, CvtRZI_FTZ>;
// nearbyint and rint are implemented as rounding to nearest even. This isn't
// strictly correct, because it causes us to ignore the rounding mode. But it
// matches what CUDA's "libm" does.
defm : CVT_ROUND<fnearbyint, CvtRNI, CvtRNI_FTZ>;
defm : CVT_ROUND<frint, CvtRNI, CvtRNI_FTZ>;
//-----------------------------------
// Control-flow
//-----------------------------------
let isTerminator=1 in {
let isReturn=1, isBarrier=1 in
def Return : BasicNVPTXInst<(outs), (ins), "ret", [(retglue)]>;
let isBranch=1 in {
def CBranch : NVPTXInst<(outs),
(ins B1:$a, brtarget:$target, BranchFlag:$not),
"@${not}$a bra \t$target;",
[(brcond i1:$a, bb:$target)]>;
let isBarrier=1 in
def GOTO : BasicNVPTXInst<(outs), (ins brtarget:$target),
"bra.uni", [(br bb:$target)]>;
}
}
// trap instruction
def trapinst : BasicNVPTXInst<(outs), (ins), "trap", [(trap)]>, Requires<[noPTXASUnreachableBug]>;
// Emit an `exit` as well to convey to ptxas that `trap` exits the CFG.
// This won't be necessary in a future version of ptxas.
def trapexitinst : NVPTXInst<(outs), (ins), "trap; exit;", [(trap)]>, Requires<[hasPTXASUnreachableBug]>;
// brkpt instruction
def debugtrapinst : BasicNVPTXInst<(outs), (ins), "brkpt", [(debugtrap)]>;
def SDTDynAllocaOp :
SDTypeProfile<1, 2, [SDTCisSameAs<0, 1>, SDTCisInt<1>, SDTCisVT<2, i32>]>;
def dyn_alloca :
SDNode<"NVPTXISD::DYNAMIC_STACKALLOC", SDTDynAllocaOp,
[SDNPHasChain, SDNPSideEffect]>;
foreach t = [I32RT, I64RT] in {
def DYNAMIC_STACKALLOC # t.Size :
BasicNVPTXInst<(outs t.RC:$ptr),
(ins t.RC:$size, i32imm:$align),
"alloca.u" # t.Size,
[(set t.Ty:$ptr, (dyn_alloca t.Ty:$size, timm:$align))]>,
Requires<[hasPTX<73>, hasSM<52>]>;
}
//
// BRX
//
let isTerminator = 1, isBranch = 1, isIndirectBranch = 1, isNotDuplicable = 1 in {
def BRX_START :
NVPTXInst<(outs), (ins i32imm:$id), "$$L_brx_$id: .branchtargets">;
def BRX_ITEM :
NVPTXInst<(outs), (ins brtarget:$target), "\t$target,">;
def BRX_END :
NVPTXInst<(outs), (ins brtarget:$target, B32:$val, i32imm:$id),
"\t$target;\n\tbrx.idx \t$val, $$L_brx_$id;"> {
let isBarrier = 1;
}
}
foreach a_type = ["s", "u"] in {
foreach b_type = ["s", "u"] in {
def DOT4_ # a_type # b_type :
BasicNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b, B32:$c),
"dp4a." # a_type # "32." # b_type # "32",
[(set i32:$dst,
(!cast<Intrinsic>("int_nvvm_idp4a_" # a_type # "_" # b_type)
i32:$a, i32:$b, i32:$c))]>,
Requires<[hasDotInstructions]>;
foreach is_hi = [0, -1] in {
defvar lohi_suffix = !if(is_hi, "hi", "lo");
def DOT2_ # lohi_suffix # _ # a_type # b_type :
BasicNVPTXInst<(outs B32:$dst),
(ins B32:$a, B32:$b, B32:$c),
"dp2a." # lohi_suffix # "." # a_type # "32." # b_type # "32",
[(set i32:$dst,
(!cast<Intrinsic>("int_nvvm_idp2a_" # a_type # "_" # b_type)
i32:$a, i32:$b, is_hi, i32:$c))]>,
Requires<[hasDotInstructions]>;
}
}
}
//
// Stack Manipulation
//
def SDTStackRestore : SDTypeProfile<0, 1, [SDTCisInt<0>]>;
def stackrestore :
SDNode<"NVPTXISD::STACKRESTORE", SDTStackRestore,
[SDNPHasChain, SDNPSideEffect]>;
def stacksave :
SDNode<"NVPTXISD::STACKSAVE", SDTIntLeaf,
[SDNPHasChain, SDNPSideEffect]>;
let Predicates = [hasPTX<73>, hasSM<52>] in {
foreach t = [I32RT, I64RT] in {
def STACKRESTORE_ # t.Size :
BasicNVPTXInst<(outs), (ins t.RC:$ptr),
"stackrestore.u" # t.Size,
[(stackrestore t.Ty:$ptr)]>;
def STACKSAVE_ # t.Size :
BasicNVPTXInst<(outs t.RC:$dst), (ins),
"stacksave.u" # t.Size,
[(set t.Ty:$dst, (t.Ty stacksave))]>;
}
}
include "NVPTXIntrinsics.td"
////////////////////////////////////////////////////////////////////////////////
// PTX Fence instructions
////////////////////////////////////////////////////////////////////////////////
class NVPTXFenceInst<string scope, string sem, Predicate ptx>:
BasicNVPTXInst<(outs), (ins), "fence."#sem#"."#scope>,
Requires<[ptx, hasSM<70>]>;
foreach scope = ["sys", "gpu", "cluster", "cta"] in {
def atomic_thread_fence_seq_cst_#scope: NVPTXFenceInst<scope, "sc", hasPTX<60>>;
def atomic_thread_fence_acq_rel_#scope: NVPTXFenceInst<scope, "acq_rel", hasPTX<60>>;
def atomic_thread_fence_acquire_#scope: NVPTXFenceInst<scope, "acquire", hasPTX<87>>;
def atomic_thread_fence_release_#scope: NVPTXFenceInst<scope, "release", hasPTX<87>>;
}
// Perform substitution if fma only has one use, and also if instruction has
// nnan instruction flag
def NVPTX_fma_oneuse_and_nnan : PatFrag<(ops node:$a, node:$b, node:$c),
(fma node:$a, node:$b, node:$c), [{
return N->hasOneUse() && N->getFlags().hasNoNaNs();
}]>;
// fmaxnum/fmaximumnum differentiate between signed and unsigned zeros, so this
// PatFrag is for a fmaxnum/fmaximumnum node with nsz
def NVPTX_fmaxnum_or_fmaximumnum_nsz : PatFrag<(ops node:$a, node:$b),
(fmaxnum_or_fmaximumnum node:$a, node:$b), [{
return N->getFlags().hasNoSignedZeros();
}]>;
class FMARELUInst<RegTyInfo t, bit allow_ftz, PatFrag zero_pat>
: BasicFlagsNVPTXInst<(outs t.RC:$dst), (ins t.RC:$a, t.RC:$b, t.RC:$c),
!if(allow_ftz, (ins FTZFlag:$ftz), (ins)),
"fma.rn" # !if(allow_ftz, "$ftz", "") # ".relu." # t.PtxType,
[(set t.Ty:$dst, (NVPTX_fmaxnum_or_fmaximumnum_nsz (NVPTX_fma_oneuse_and_nnan t.Ty:$a, t.Ty:$b, t.Ty:$c), zero_pat))]>;
let Predicates = [useFP16Math, hasPTX<70>, hasSM<80>] in {
def FMARELU_F16 : FMARELUInst<F16RT, true, fpimm_0>;
def FMARELU_F16X2 : FMARELUInst<F16X2RT, true, zeroinitializer<v2f16>>;
}
let Predicates = [hasBF16Math, hasPTX<70>, hasSM<80>] in {
def FMARELU_BF16 : FMARELUInst<BF16RT, false, fpimm_0>;
def FMARELU_BF16X2 : FMARELUInst<BF16X2RT, false, zeroinitializer<v2bf16>>;
}