Files
gps/tools/local/circt/include/llvm/IR/IntrinsicsNVVM.td
T
2026-07-12 11:01:51 +03:00

3466 lines
141 KiB
TableGen

//===- IntrinsicsNVVM.td - Defines NVVM intrinsics ---------*- tablegen -*-===//
//
// 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 defines all of the NVVM-specific intrinsics for use with NVPTX.
//
//===----------------------------------------------------------------------===//
//===----------------------------------------------------------------------===//
// Guidelines on NVPTX Intrinsic design
//===----------------------------------------------------------------------===//
//
// The NVPTX intrinsics are used to model instructions in the PTX ISA.
// While simpler intrinsics can represent certain features effectively,
// more complex instructions like TMA and MMA are not as straightforward
// to model. A single variant of these complex instructions can expand
// into hundreds of intrinsics. Additionally, any expansion in the
// corresponding ISA can exponentially increase these numbers, making it
// difficult to manage them in the IR and backend passes. Therefore,
// a careful design of intrinsic interfaces can ease maintenance and
// contribute to a sustainable, long-term solution.
//
// The default approach is to have a 1:1 match between the intrinsic and
// the instruction where the instruction suffixes map to the intrinsic name
// and the instruction arguments map to the intrinsic arguments or return
// value.
//
// However, when there are too many instruction/intrinsic variants like
// the TMA/MMA family, it is desirable to encode some variants as a
// constant argument, referred to as 'flags'.
// TODO: Add a guideline to quantify the metric on 'how many intrinsics' here.
//
// Below are a set of guidelines that may help in choosing
// an appropriate design for the complex intrinsics:
//
// 1. Each flag argument represents one set of instruction modifiers.
// These flags are compile-time integer constants.
//
// 2. When an intrinsic uses flags, document it with details of the
// flag usage in the ``NVPTXUsage.rst`` file.
// 3. Annotate all flag arguments with ImmArg<ArgIdx<>>.
// 4. Place the flag arguments at the end of the (actual)argument list.
//
// 5. Use `i1` for boolean flags and `i8` for others. Usually,
// the `i8` types represent an `enum` encoding the family of
// modifiers.
// 6. Note that, the specific variant for non-boolean flags may not be
// obvious in the IR. So, maintain consistency between the enum value
// definitions and their usage in the backend.
// * Provide a meaningful default value in the enums wherever applicable.
// * TODO: Investigate auto-upgrade capability for intrinsics
// when only flag value mappings change.
//
// 7. Identify the key features of an intrinsic and distinguish between
// first-order and supplementary information. Typically, encoding the
// first-order information in the intrinsic name while using flags
// for supplementary details improves readability.
// For example:
//
// i. For MMA intrinsics, 'dense' vs. 'sparse' is a fundamental feature,
// whereas an optional scaling applied to matrices is relatively secondary.
//
// ii. For TMAs, the mode of copy (e.g., 'Tile' or 'Im2col') is a first-order
// information, while features like an optional cache hint tend to be
// secondary.
//
// 8. If there are invalid combinations within a set of modifiers, avoid
// encoding them as flags, as much as possible. This helps reduce the
// need for error handling of unsupported cases in the backend.
// For example, some 'cvt' intrinsics support only a subset of the
// possible rounding modes; so it is preferable not to encode the
// rounding modes as flags.
// 9. Similarly, when there are invalid combinations across a set of
// modifiers, avoid encoding them as flags to prevent additional
// complexity in error handling.
//
// 10. Maintain a consistent design within an intrinsic family, including
// argument ordering as well as the usage and ordering of flags.
// 11. When designing an intrinsic corresponding to an instruction or its variant,
// consider the entire instruction family. This may reveal common features
// that can be modelled consistently across the family.
//
// In summary, strive to balance the aspects mentioned above, to achieve
// a scalable design with maximum readability.
//===----------------------------------------------------------------------===//
// The following intrinsics were once defined here, but are now auto-upgraded
// to target-generic LLVM intrinsics.
//
// * llvm.nvvm.brev32 --> llvm.bitreverse.i32
// * llvm.nvvm.brev64 --> llvm.bitreverse.i64
// * llvm.nvvm.clz.i --> llvm.ctlz.i32
// * llvm.nvvm.clz.ll --> trunc i64 llvm.ctlz.i64(x) to i32
// * llvm.nvvm.popc.i --> llvm.ctpop.i32
// * llvm.nvvm.popc.ll --> trunc i64 llvm.ctpop.i64 to i32
// * llvm.nvvm.abs.i --> llvm.abs.i32(x, true)
// * llvm.nvvm.abs.ll --> llvm.abs.i64(x, true)
// * llvm.nvvm.max.i --> select(x sge y, x, y)
// * llvm.nvvm.max.ll --> ibid.
// * llvm.nvvm.max.ui --> select(x uge y, x, y)
// * llvm.nvvm.max.ull --> ibid.
// * llvm.nvvm.max.i --> select(x sle y, x, y)
// * llvm.nvvm.max.ll --> ibid.
// * llvm.nvvm.max.ui --> select(x ule y, x, y)
// * llvm.nvvm.max.ull --> ibid.
// * llvm.nvvm.h2f --> llvm.convert.to.fp16.f32
// * llvm.nvvm.bitcast.f2i --> bitcast
// * llvm.nvvm.bitcast.i2f --> ibid.
// * llvm.nvvm.bitcast.d2ll --> ibid.
// * llvm.nvvm.bitcast.ll2d --> ibid.
// * llvm.nvvm.ptr.gen.to.global --> addrspacecast
// * llvm.nvvm.ptr.gen.to.shared --> ibid.
// * llvm.nvvm.ptr.gen.to.constant --> ibid.
// * llvm.nvvm.ptr.gen.to.local --> ibid.
// * llvm.nvvm.ptr.gen.to.param --> ibid.
// * llvm.nvvm.ptr.global.to.gen --> ibid.
// * llvm.nvvm.ptr.shared.to.gen --> ibid.
// * llvm.nvvm.ptr.constant.to.gen --> ibid.
// * llvm.nvvm.ptr.local.to.gen --> ibid.
// * llvm.nvvm.ptr.param.to.gen --> ibid.
// * llvm.nvvm.ldg.global.i --> load addrspace(1) !load.invariant
// * llvm.nvvm.ldg.global.f --> ibid.
// * llvm.nvvm.ldg.global.p --> ibid.
// * llvm.nvvm.swap.lo.hi.b64 --> llvm.fshl(x, x, 32)
// * llvm.nvvm.atomic.load.inc.32 --> atomicrmw uinc_wrap
// * llvm.nvvm.atomic.load.dec.32 --> atomicrmw udec_wrap
// * llvm.nvvm.barrier0 --> llvm.nvvm.barrier.cta.sync.aligned.all(0)
// * llvm.nvvm.barrier.n --> llvm.nvvm.barrier.cta.sync.aligned.all(x)
// * llvm.nvvm.bar.sync --> llvm.nvvm.barrier.cta.sync.aligned.all(x)
// * llvm.nvvm.barrier --> llvm.nvvm.barrier.cta.sync.aligned(x, y)
// * llvm.nvvm.barrier.sync --> llvm.nvvm.barrier.cta.sync.all(x)
// * llvm.nvvm.barrier.sync.cnt --> llvm.nvvm.barrier.cta.sync(x, y)
// * llvm.nvvm.barrier0.popc --> llvm.nvvm.barrier.cta.red.popc.aligned.all(0, c)
// * llvm.nvvm.barrier0.and --> llvm.nvvm.barrier.cta.red.and.aligned.all(0, z)
// * llvm.nvvm.barrier0.or --> llvm.nvvm.barrier.cta.red.or.aligned.all(0, z)
def llvm_global_ptr_ty : LLVMQualPointerType<1>; // (global)ptr
def llvm_shared_ptr_ty : LLVMQualPointerType<3>; // (shared)ptr
def llvm_constant_ptr_ty: LLVMQualPointerType<4>; // (const)ptr
def llvm_local_ptr_ty : LLVMQualPointerType<5>; // (local)ptr
def llvm_tmem_ptr_ty : LLVMQualPointerType<6>; // (tensor memory)ptr
def llvm_shared_cluster_ptr_ty : LLVMQualPointerType<7>; // (shared_cluster)ptr
//
// MISC
//
defvar WARP_SIZE = 32;
// Note: the maximum grid size in the x-dimension is the lower value of 65535
// on sm_20. We conservatively use the larger value here as it required for
// sm_30+ and also correct for sm_20.
defvar MAX_GRID_SIZE_X = 0x7fffffff;
defvar MAX_GRID_SIZE_Y = 0xffff;
defvar MAX_GRID_SIZE_Z = 0xffff;
defvar MAX_BLOCK_SIZE_X = 1024;
defvar MAX_BLOCK_SIZE_Y = 1024;
defvar MAX_BLOCK_SIZE_Z = 64;
// Helper class that concatenates list elements with
// a given separator 'sep' and returns the result.
// Handles empty strings.
class StrJoin<string sep, list<string> str_list> {
string ret = !foldl("", str_list, a, b,
!if(!eq(a, ""), b, !if(!eq(b, ""), a, !strconcat(a, sep, b))));
}
// Helper class that represents a 'fragment' of an NVPTX *MMA instruction.
// Geom: m<M>n<N>k<K>. E.g. m8n32k16
// Frag: [a|b|c|d] ([x1|x2|x4] for ldmatrix)
// PtxEltType: PTX type for the element.
class WMMA_REGS<string Geom, string Frag, string PtxEltType, bit IsSparse = false> {
string geom = Geom;
string frag = Frag;
string ptx_elt_type = PtxEltType;
string gft = Geom#":"#Frag#":"#ptx_elt_type;
string gf = Geom#":"#Frag;
string ft = frag#":"#ptx_elt_type;
bit isSparse = IsSparse;
list<LLVMType> regs = !if(!eq(isSparse, true),
!cond(
// mma sparse ops use other fragments for some arguments
!eq(gft,"m16n8k16:a:bf16") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:a:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:b:bf16") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:b:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:c:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k16:d:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k32:a:bf16") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:f16") : !listsplat(llvm_v2f16_ty, 4),
!eq(gft,"m16n8k32:b:bf16") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:b:f16") : !listsplat(llvm_v2f16_ty, 4),
!eq(gft,"m16n8k32:c:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k32:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k32:d:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k32:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k16:a:tf32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k16:b:tf32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k16:c:tf32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k16:d:tf32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k8:a:tf32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k8:b:tf32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k8:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k8:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k32:a:u8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:a:s8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:u8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:s8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:d:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:u8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:s8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:e4m3") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:e5m2") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:e3m2") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:e2m3") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:e2m1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:u8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:s8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:e4m3") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:e5m2") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:e3m2") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:e2m3") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:e2m1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:c:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k64:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k64:d:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k64:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k64:a:u4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k64:a:s4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k64:b:u4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k64:b:s4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k64:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:d:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:a:u4") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:a:s4") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:a:e2m1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:b:u4") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:b:s4") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:b:e2m1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k128:d:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:d:f32") : !listsplat(llvm_float_ty, 4),
),
!cond(
// mma fp ops use smaller fragments than wmma fp ops
!eq(gft,"m8n8k4:a:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m8n8k4:b:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k8:a:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k8:b:f16") : [llvm_v2f16_ty],
!eq(gft,"m16n8k8:c:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k8:d:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k8:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k8:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k16:a:f16") : !listsplat(llvm_v2f16_ty, 4),
!eq(gft,"m16n8k16:b:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:c:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:d:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k16:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k16:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k4:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k4:d:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k32:c:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k32:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k32:d:f16") : !listsplat(llvm_v2f16_ty, 2),
!eq(gft,"m16n8k32:d:f32") : !listsplat(llvm_float_ty, 4),
// mma.block_scale e2m1 (mxf4, mxf4nvf4) -> f32 @ m16n8k64
!eq(gft,"m16n8k64:c:f32") : !listsplat(llvm_float_ty, 4),
!eq(gft,"m16n8k64:d:f32") : !listsplat(llvm_float_ty, 4),
// wmma fp16 -> fp16/fp32 @ m16n16k16/m8n32k16/m32n8k16
// All other supported geometries use the same fragment format for f32 and
// f16, so we only need to consider {fragment, type}.
!eq(ft,"a:f16") : !listsplat(llvm_v2f16_ty, 8),
!eq(ft,"b:f16") : !listsplat(llvm_v2f16_ty, 8),
!eq(ft,"c:f16") : !listsplat(llvm_v2f16_ty, 4),
!eq(ft,"d:f16") : !listsplat(llvm_v2f16_ty, 4),
!eq(ft,"c:f32") : !listsplat(llvm_float_ty, 8),
!eq(ft,"d:f32") : !listsplat(llvm_float_ty, 8),
// wmma tf32 -> s32 @ m16n16k8
!eq(gft,"m16n16k8:a:tf32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n16k8:b:tf32") : !listsplat(llvm_i32_ty, 4),
// mma tf32 -> s32 @ m16n16k8/m16n8k8
!eq(gft,"m16n8k4:a:tf32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k4:b:tf32") : [llvm_i32_ty],
!eq(gft,"m16n8k8:a:tf32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k8:b:tf32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m8n8k4:a:f64") : [llvm_double_ty],
!eq(gft,"m8n8k4:b:f64") : [llvm_double_ty],
!eq(gft,"m8n8k4:c:f64") : !listsplat(llvm_double_ty, 2),
!eq(gft,"m8n8k4:d:f64") : !listsplat(llvm_double_ty, 2),
!eq(gft,"m16n8k4:a:f64") : !listsplat(llvm_double_ty, 2),
!eq(gft,"m16n8k4:b:f64") : [llvm_double_ty],
!eq(gft,"m16n8k4:c:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k4:d:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k8:a:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k8:b:f64") : !listsplat(llvm_double_ty, 2),
!eq(gft,"m16n8k8:c:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k8:d:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k16:a:f64") : !listsplat(llvm_double_ty, 8),
!eq(gft,"m16n8k16:b:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k16:c:f64") : !listsplat(llvm_double_ty, 4),
!eq(gft,"m16n8k16:d:f64") : !listsplat(llvm_double_ty, 4),
// wmma bf16 -> s32 @ m16n16k16/m8n32k16/m32n8k16
!eq(gft,"m16n16k16:a:bf16") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n16k16:b:bf16") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m8n32k16:a:bf16") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m8n32k16:b:bf16") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m32n8k16:a:bf16") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m32n8k16:b:bf16") : !listsplat(llvm_i32_ty, 2),
// mma bf16 -> s32 @ m16n8k16/m16n8k8
!eq(gft,"m16n8k16:a:bf16") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k16:b:bf16") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k8:a:bf16") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k8:b:bf16") : [llvm_i32_ty],
// wmma u8/s8 -> s32 @ m16n16k16/m8n32k16/m32n8k16
!eq(gft,"m16n16k16:a:u8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n16k16:a:s8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n16k16:b:u8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n16k16:b:s8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n16k16:c:s32") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m16n16k16:d:s32") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m8n32k16:a:u8") : [llvm_i32_ty],
!eq(gft,"m8n32k16:a:s8") : [llvm_i32_ty],
!eq(gft,"m8n32k16:b:u8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m8n32k16:b:s8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m8n32k16:c:s32") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m8n32k16:d:s32") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m32n8k16:a:u8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m32n8k16:a:s8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m32n8k16:b:u8") : [llvm_i32_ty],
!eq(gft,"m32n8k16:b:s8") : [llvm_i32_ty],
!eq(gft,"m32n8k16:c:s32") : !listsplat(llvm_i32_ty, 8),
!eq(gft,"m32n8k16:d:s32") : !listsplat(llvm_i32_ty, 8),
// mma u8/s8 -> s32 @ m8n8k16/m16n8k16/m16n8k32
!eq(gft,"m8n8k16:a:u8") : [llvm_i32_ty],
!eq(gft,"m8n8k16:a:s8") : [llvm_i32_ty],
!eq(gft,"m8n8k16:b:u8") : [llvm_i32_ty],
!eq(gft,"m8n8k16:b:s8") : [llvm_i32_ty],
!eq(gft,"m8n8k16:c:s32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m8n8k16:d:s32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:a:u8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:a:s8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:b:u8") : [llvm_i32_ty],
!eq(gft,"m16n8k16:b:s8") : [llvm_i32_ty],
!eq(gft,"m16n8k16:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k16:d:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:u8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:s8") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:b:u8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:s8") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:d:s32") : !listsplat(llvm_i32_ty, 4),
// wmma/mma u4/s4 -> s32 @ m8n8k32 (u4/s4)
!eq(gft,"m8n8k32:a:u4") : [llvm_i32_ty],
!eq(gft,"m8n8k32:a:s4") : [llvm_i32_ty],
!eq(gft,"m8n8k32:b:u4") : [llvm_i32_ty],
!eq(gft,"m8n8k32:b:s4") : [llvm_i32_ty],
!eq(gft,"m8n8k32:c:s32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m8n8k32:d:s32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:a:u4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:a:s4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:u4") : [llvm_i32_ty],
!eq(gft,"m16n8k32:b:s4") : [llvm_i32_ty],
!eq(gft,"m16n8k32:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:d:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:u4") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:a:s4") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:u4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k64:b:s4") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k64:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:d:s32") : !listsplat(llvm_i32_ty, 4),
// mma e4m3/e5m2 -> f16/f32 @ m16n8k16
!eq(gft,"m16n8k16:a:e4m3") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:a:e5m2") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k16:b:e4m3") : [llvm_i32_ty],
!eq(gft,"m16n8k16:b:e5m2") : [llvm_i32_ty],
// mma e4m3/e5m2/e3m2/e2m3/e2m1 -> f32 @ m16n8k32
!eq(gft,"m16n8k32:a:e4m3") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:e5m2") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:e3m2") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:e2m3") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:a:e2m1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k32:b:e4m3") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:e5m2") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:e3m2") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:e2m3") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k32:b:e2m1") : !listsplat(llvm_i32_ty, 2),
// mma e2m1 -> f32 @m16n8k64
!eq(gft,"m16n8k64:a:e2m1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k64:b:e2m1") : !listsplat(llvm_i32_ty, 2),
// wmma/mma b1 -> s32 @ m8n8k128(b1)
!eq(gft,"m8n8k128:a:b1") : [llvm_i32_ty],
!eq(gft,"m8n8k128:b:b1") : [llvm_i32_ty],
!eq(gft,"m8n8k128:c:s32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m8n8k128:d:s32") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k128:a:b1") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k128:b:b1") : [llvm_i32_ty],
!eq(gft,"m16n8k128:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k128:d:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k256:a:b1") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k256:b:b1") : !listsplat(llvm_i32_ty, 2),
!eq(gft,"m16n8k256:c:s32") : !listsplat(llvm_i32_ty, 4),
!eq(gft,"m16n8k256:d:s32") : !listsplat(llvm_i32_ty, 4),
// ldmatrix b16 -> s32 @ m8n8
!eq(gf,"m8n8:x1") : !listsplat(llvm_i32_ty, 1),
!eq(gf,"m8n8:x2") : !listsplat(llvm_i32_ty, 2),
!eq(gf,"m8n8:x4") : !listsplat(llvm_i32_ty, 4),
// ldmatrix b8, b8x16.b6x16_p32, b8x16.b4x16_p64 -> s32 @ m16n16
!eq(gf,"m16n16:x1") : !listsplat(llvm_i32_ty, 2),
!eq(gf,"m16n16:x2") : !listsplat(llvm_i32_ty, 4),
// ldmatrix b8x16.b6x16_p32, b8x16.b4x16_p64 -> s32 @ m8n16
!eq(gf,"m8n16:x1") : !listsplat(llvm_i32_ty, 1),
!eq(gf,"m8n16:x2") : !listsplat(llvm_i32_ty, 2),
!eq(gf,"m8n16:x4") : !listsplat(llvm_i32_ty, 4),
// stmatrix b8 -> s32 @ m16n8
!eq(gf,"m16n8:x1") : !listsplat(llvm_i32_ty, 1),
!eq(gf,"m16n8:x2") : !listsplat(llvm_i32_ty, 2),
!eq(gf,"m16n8:x4") : !listsplat(llvm_i32_ty, 4),
)
);
}
class WMMA_NAME_LDST<string Op, WMMA_REGS Frag, string Layout, int WithStride> {
string intr_name = "llvm.nvvm.wmma."
# Frag.geom
# "." # Op
# "." # Frag.frag
# "." # Layout
# !if(WithStride, ".stride", "")
# "." # Frag.ptx_elt_type
;
// TODO(tra): record name should ideally use the same field order as the intrinsic.
// E.g. string record = !subst("llvm", "int",
// !subst(".", "_", llvm));
string record_name = "int_nvvm_wmma_"
# Frag.geom
# "_" # Op
# "_" # Frag.frag
# "_" # Frag.ptx_elt_type
# "_" # Layout
# !if(WithStride, "_stride", "");
}
class MMA_SIGNATURE<WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D> {
list<WMMA_REGS> id_frags = !cond(
// FP8/F8F6F4 ops are identified by A,B inputs & accomulator & result type.
!or(!eq(A.ptx_elt_type, "e4m3"),
!eq(A.ptx_elt_type, "e5m2"),
!eq(A.ptx_elt_type, "e3m2"),
!eq(A.ptx_elt_type, "e2m3"),
!eq(A.ptx_elt_type, "e2m1")): [D, A, B, C],
// FP16 ops are identified by accumulator & result type.
!eq(A.ptx_elt_type, "f16") : [D, C],
// other ops are identified by input types.
!ne(A.ptx_elt_type, B.ptx_elt_type): [A, B],
true: [A]
);
string ret = !foldl("", id_frags, a, b, !strconcat(a, "_", b.ptx_elt_type));
}
class WMMA_NAME<string ALayout, string BLayout, int Satfinite, string Rnd, string b1op,
WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D> {
string signature = MMA_SIGNATURE<A, B, C, D>.ret;
string record_name = "int_nvvm_wmma_"
# A.geom
# "_mma"
# !subst(".", "_", b1op)
# "_" # ALayout
# "_" # BLayout
# !if(!ne(Rnd, ""), !strconcat("_", Rnd), "")
# signature
# !if(Satfinite, "_satfinite", "");
}
class MMA_NAME<string ALayout, string BLayout, int Satfinite, string b1op, string Kind,
WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D> {
string signature = MMA_SIGNATURE<A, B, C, D>.ret;
string record_name = "int_nvvm_mma"
# !subst(".", "_", b1op)
# "_" # A.geom
# "_" # ALayout
# "_" # BLayout
# !if(!ne(Kind, ""), !strconcat("_", !subst("::", "_", Kind)), "")
# !if(Satfinite, "_satfinite", "")
# signature;
}
class MMA_BLOCK_SCALE_NAME<string Kind, string SType, string ScaleVecSize,
WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D> {
string signature = MMA_SIGNATURE<A, B, C, D>.ret;
string record_name = "int_nvvm_mma_block_scale"
# "_" # A.geom
# "_row_col"
# "_" # Kind
# !subst(".", "_", ScaleVecSize)
# signature
# "_" # SType;
}
class MMA_SP_NAME<string Metadata, string Kind, int Satfinite,
WMMA_REGS A, WMMA_REGS B,
WMMA_REGS C, WMMA_REGS D> {
string signature = MMA_SIGNATURE<A, B, C, D>.ret;
string record_name = "int_nvvm_mma"
# "_" # !subst("::", "_", Metadata)
# "_" # A.geom
# "_row_col"
# !if(!ne(Kind, ""), !strconcat("_", !subst("::", "_", Kind)), "")
# !if(Satfinite, "_satfinite", "")
# signature;
}
class MMA_SP_BLOCK_SCALE_NAME<string Kind, string SType, string ScaleVecSize,
WMMA_REGS A, WMMA_REGS B,
WMMA_REGS C, WMMA_REGS D> {
string signature = MMA_SIGNATURE<A, B, C, D>.ret;
string record_name = "int_nvvm_mma_sp_ordered_metadata_block_scale"
# "_" # A.geom
# "_row_col"
# "_" # Kind
# !subst(".", "_", ScaleVecSize)
# signature
# "_" # SType;
}
// Helper class that takes an intrinsic name and construct a record name.
// Additionally, sets `intr_name` to be non-empty if the default name assigned
// to this intrinsic will not match the name given.
class IntrinsicName<string name> {
string record_name = !subst(".", "_",
!subst("llvm.", "int_", name));
// Use explicit intrinsic name if it has an _ in it, else rely on LLVM
// assigned default name.
string intr_name = !if(!ne(!find(name, "_"), -1), name, "");
}
class LDMATRIX_NAME<WMMA_REGS Frag, int Trans> {
defvar name = "llvm.nvvm.ldmatrix.sync.aligned"
# "." # Frag.geom
# "." # Frag.frag
# !if(Trans, ".trans", "")
# "." # Frag.ptx_elt_type
;
string intr_name = IntrinsicName<name>.intr_name;
string record_name = IntrinsicName<name>.record_name;
}
class STMATRIX_NAME<WMMA_REGS Frag, int Trans> {
defvar name = "llvm.nvvm.stmatrix.sync.aligned"
# "." # Frag.geom
# "." # Frag.frag
# !if(Trans, ".trans", "")
# "." # Frag.ptx_elt_type
;
string intr_name = IntrinsicName<name>.intr_name;
string record_name = IntrinsicName<name>.record_name;
}
// Generates list of 4-tuples of WMMA_REGS representing a valid MMA op.
// Geom: list of supported geometries.
// TypeN: PTX type of the corresponding fragment's element.
// TypeB and TypeD may be empty if it must match that of TypeA or TypeC.
class MMA_OPS<list<string> Geom, list<string> TypeA, list<string> TypeB,
list<string> TypeC, list<string> TypeD, bit IsSparse = false> {
list<list<WMMA_REGS>> ret =
!foldl([]<list<WMMA_REGS>>, Geom, t1, geom, !listconcat(t1,
!foldl([]<list<WMMA_REGS>>, TypeA, t2, type_a, !listconcat(t2,
!foldl([]<list<WMMA_REGS>>, !if(!size(TypeB), TypeB, [type_a]), t3, type_b, !listconcat(t3,
!foldl([]<list<WMMA_REGS>>, TypeC, t4, type_c, !listconcat(t4,
!foldl([]<list<WMMA_REGS>>, !if(!size(TypeD), TypeD, [type_c]), t5, type_d, !listconcat(t5,
[[WMMA_REGS<geom, "a", type_a, IsSparse>,
WMMA_REGS<geom, "b", type_b, IsSparse>,
WMMA_REGS<geom, "c", type_c, IsSparse>,
WMMA_REGS<geom, "d", type_d, IsSparse>]]))))))))));
// Debugging aid for readable representation of the list above.
list<list<string>> ops = !foreach(x, ret, [x[0].gft, x[1].gft, x[2].gft, x[3].gft]);
}
class MMA_LDST_OPS<list<string> Geom, list<string> Frags, list<string> Types> {
list<WMMA_REGS> ret =
!foldl([]<WMMA_REGS>, Geom, t1, geom, !listconcat(t1,
!foldl([]<WMMA_REGS>, Frags, t2, frag, !listconcat(t2,
!foldl([]<WMMA_REGS>, Types, t3, type, !listconcat(t3,
[WMMA_REGS<geom, frag, type>]))))));
// Debugging aid for readable representation of the list above.
list<string> ops = !foreach(x, ret, x.gft);
}
class LDMATRIX_OPS<list<string> Geom, list<string> Frags, list<string> Types> {
list<WMMA_REGS> ret =
!foldl([]<WMMA_REGS>, Geom, t1, geom, !listconcat(t1,
!foldl([]<WMMA_REGS>, Frags, t2, frag, !listconcat(t2,
!foldl([]<WMMA_REGS>, Types, t3, type, !listconcat(t3,
[WMMA_REGS<geom, frag, type>]))))));
// Debugging aid for readable representation of the list above.
list<string> ops = !foreach(x, ret, x.gft);
}
class STMATRIX_OPS<list<string> Geom, list<string> Frags, list<string> Types> {
list<WMMA_REGS> ret =
!foldl([]<WMMA_REGS>, Geom, t1, geom, !listconcat(t1,
!foldl([]<WMMA_REGS>, Frags, t2, frag, !listconcat(t2,
!foldl([]<WMMA_REGS>, Types, t3, type, !listconcat(t3,
[WMMA_REGS<geom, frag, type>]))))));
// Debugging aid for readable representation of the list above.
list<string> ops = !foreach(x, ret, x.gft);
}
// Creates list of valid combinations of fragments. This is the main list that
// drives generation of corresponding intrinsics and instructions.
class NVVM_MMA_OPS {
list<list<WMMA_REGS>> tf32_wmma_ops = MMA_OPS<
["m16n16k8"],
["tf32"], [], ["f32"], []>.ret;
list<list<WMMA_REGS>> bf16_wmma_ops = MMA_OPS<
["m16n16k16", "m32n8k16", "m8n32k16"],
["bf16"], [], ["f32"], []>.ret;
list<list<WMMA_REGS>> f64_wmma_ops = MMA_OPS<
["m8n8k4"],
["f64"], [], ["f64"], []>.ret;
list<list<WMMA_REGS>> fp_wmma_ops = MMA_OPS<
["m16n16k16", "m32n8k16", "m8n32k16"],
["f16"], [], ["f16", "f32"], ["f16", "f32"]>.ret;
list<list<WMMA_REGS>> int_wmma_ops = MMA_OPS<
["m16n16k16", "m32n8k16", "m8n32k16"],
["s8", "u8"], [], ["s32"], []>.ret;
list<list<WMMA_REGS>> subint_wmma_ops = MMA_OPS<
["m8n8k32"],
["s4", "u4"], [], ["s32"], []>.ret;
list<list<WMMA_REGS>> bit_wmma_ops = MMA_OPS<
["m8n8k128"],
["b1"], [], ["s32"], []>.ret;
list<list<WMMA_REGS>> all_wmma_ops = !listconcat(
tf32_wmma_ops, bf16_wmma_ops, f64_wmma_ops,
fp_wmma_ops, int_wmma_ops, subint_wmma_ops, bit_wmma_ops);
list<list<WMMA_REGS>> tf32_mma_ops = MMA_OPS<
["m16n8k4", "m16n8k8"],
["tf32"], [], ["f32"], []>.ret;
list<list<WMMA_REGS>> bf16_mma_ops = MMA_OPS<
["m16n8k16", "m16n8k8"],
["bf16"], [], ["f32"], []>.ret;
list<list<WMMA_REGS>> f64_mma_ops = MMA_OPS<
["m8n8k4", "m16n8k4", "m16n8k8", "m16n8k16"],
["f64"], [], ["f64"], []>.ret;
list<list<WMMA_REGS>> fp_mma_ops = MMA_OPS<
["m8n8k4", "m16n8k8", "m16n8k16"],
["f16"], [], ["f16", "f32"], ["f16", "f32"]>.ret;
list<list<WMMA_REGS>> int_mma_ops = MMA_OPS<
["m8n8k16", "m16n8k16", "m16n8k32"],
["s8", "u8"], ["s8", "u8"], ["s32"], []>.ret;
// m16n8k32 fp8 variants are intersected with f8f6f4 variants
// and processed there
list<list<WMMA_REGS>> fp8_mma_ops = MMA_OPS<
["m16n8k16"],
["e4m3", "e5m2"], ["e4m3", "e5m2"],
["f16", "f32"], ["f16", "f32"]>.ret;
// it also contains e4m3/e5m2 from fp8 variants
list<list<WMMA_REGS>> f8f6f4_mma_ops = MMA_OPS<
["m16n8k32"],
["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"],
["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"],
["f16", "f32"], ["f16", "f32"]>.ret;
list<list<WMMA_REGS>> subint_mma_ops = MMA_OPS<
["m8n8k32", "m16n8k32", "m16n8k64"],
["s4", "u4"], ["s4", "u4"], ["s32"], []>.ret;
list<list<WMMA_REGS>> bit_mma_ops = MMA_OPS<
["m8n8k128", "m16n8k128", "m16n8k256"],
["b1"], [], ["s32"], []>.ret;
list<list<WMMA_REGS>> all_mma_ops = !listconcat(
tf32_mma_ops, bf16_mma_ops, f64_mma_ops,
fp_mma_ops, fp8_mma_ops, f8f6f4_mma_ops,
int_mma_ops, subint_mma_ops, bit_mma_ops);
list<list<WMMA_REGS>> mxf4_mma_ops = MMA_OPS<
["m16n8k64"], ["e2m1"], ["e2m1"], ["f32"], ["f32"]
>.ret;
list<list<WMMA_REGS>> mxf8f6f4_mma_ops = MMA_OPS<
["m16n8k32"], ["e2m1", "e2m3", "e3m2", "e5m2", "e4m3"],
["e2m1", "e2m3", "e3m2", "e5m2", "e4m3"], ["f32"], ["f32"]
>.ret;
list<list<WMMA_REGS>> all_mma_block_scale_ops = !listconcat(
mxf4_mma_ops, mxf8f6f4_mma_ops);
list<list<WMMA_REGS>> bf16_mma_sp_ops = MMA_OPS<
["m16n8k16", "m16n8k32"],
["bf16"], [], ["f32"], [], true>.ret;
list<list<WMMA_REGS>> tf32_mma_sp_ops = MMA_OPS<
["m16n8k8", "m16n8k16"],
["tf32"], [], ["f32"], [], true>.ret;
list<list<WMMA_REGS>> fp_mma_sp_ops = MMA_OPS<
["m16n8k16", "m16n8k32"],
["f16"], [], ["f16", "f32"], ["f16", "f32"], true>.ret;
list<list<WMMA_REGS>> fp8_mma_sp_ops = MMA_OPS<
["m16n8k64"],
["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"],
["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"],
["f16", "f32"], ["f16", "f32"], true>.ret;
list<list<WMMA_REGS>> subint_mma_sp_ops = MMA_OPS<
["m16n8k64", "m16n8k128"],
["s4", "u4"], ["s4", "u4"], ["s32"], [], true>.ret;
list<list<WMMA_REGS>> int_mma_sp_ops = MMA_OPS<
["m16n8k32", "m16n8k64"],
["s8", "u8"], ["s8", "u8"], ["s32"], [], true>.ret;
list<list<WMMA_REGS>> all_mma_sp_ops = !listconcat(
bf16_mma_sp_ops, tf32_mma_sp_ops, fp_mma_sp_ops, fp8_mma_sp_ops,
subint_mma_sp_ops, int_mma_sp_ops);
// combines available geoms and types for mxf4 and mxf4nvf4 kinds
list<list<WMMA_REGS>> mxf4xx_mma_sp_ops = MMA_OPS<
["m16n8k128"],
["e2m1"], ["e2m1"], ["f32"], [], true>.ret;
list<list<WMMA_REGS>> mxf8f6f4_mma_sp_ops = MMA_OPS<
["m16n8k64"],
["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"],
["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"],
["f32"], [], true>.ret;
list<list<WMMA_REGS>> all_mma_sp_block_scale_ops = !listconcat(
mxf4xx_mma_sp_ops, mxf8f6f4_mma_sp_ops);
list<WMMA_REGS> ldst_ab_ops = MMA_LDST_OPS<
["m16n16k16", "m32n8k16", "m8n32k16"],
["a", "b"], ["f16", "u8", "s8", "bf16"]>.ret;
list<WMMA_REGS> ldst_cd_ops = MMA_LDST_OPS<
["m16n16k16", "m32n8k16", "m8n32k16"],
["c", "d"], ["f16", "f32", "s32"]>.ret;
list<WMMA_REGS> ldst_tf32_ab_ops = MMA_LDST_OPS<
["m16n16k8"],
["a", "b"], ["tf32"]>.ret;
list<WMMA_REGS> ldst_tf32_cd_ops = MMA_LDST_OPS<
["m16n16k8"],
["c", "d"], ["f32"]>.ret;
list<WMMA_REGS> ldst_f64_abcd_ops = MMA_LDST_OPS<
["m8n8k4"],
["a", "b", "c", "d"], ["f64"]>.ret;
list<WMMA_REGS> ldst_subint_ab_ops = MMA_LDST_OPS<
["m8n8k32"], ["a", "b"], ["s4","u4"]>.ret;
list<WMMA_REGS> ldst_bit_ab_ops = MMA_LDST_OPS<
["m8n8k128"], ["a", "b"], ["b1"]>.ret;
list<WMMA_REGS> ldst_subint_cd_ops = MMA_LDST_OPS<
["m8n8k32", "m8n8k128"], ["c", "d"], ["s32"]>.ret;
list<WMMA_REGS> all_ldst_ops = !listconcat(ldst_ab_ops, ldst_cd_ops,
ldst_tf32_ab_ops,
ldst_tf32_cd_ops,
ldst_f64_abcd_ops,
ldst_subint_ab_ops,
ldst_bit_ab_ops,
ldst_subint_cd_ops);
// Separate A/B/C fragments (loads) from D (stores).
list<WMMA_REGS> all_ld_ops = !filter(op, all_ldst_ops, !ne(op.frag, "d"));
list<WMMA_REGS> all_st_ops = !filter(op, all_ldst_ops, !eq(op.frag, "d"));
list<WMMA_REGS> ldmatrix_b16_ops = LDMATRIX_OPS<
["m8n8"], ["x1", "x2", "x4"], ["b16"]>.ret;
list<WMMA_REGS> ldmatrix_geom_m16n16_ops = LDMATRIX_OPS<
["m16n16"], ["x1", "x2"], ["b8", "b8x16.b6x16_p32", "b8x16.b4x16_p64"]>.ret;
list<WMMA_REGS> ldmatrix_geom_m8n16_ops = LDMATRIX_OPS<
["m8n16"], ["x1", "x2", "x4"], ["b8x16.b6x16_p32", "b8x16.b4x16_p64"]>.ret;
list<WMMA_REGS> stmatrix_b16_ops = STMATRIX_OPS<
["m8n8"], ["x1", "x2", "x4"], ["b16"]>.ret;
list<WMMA_REGS> stmatrix_b8_ops = STMATRIX_OPS<
["m16n8"], ["x1", "x2", "x4"], ["b8"]>.ret;
list<WMMA_REGS> all_ldmatrix_ops = !listconcat(ldmatrix_b16_ops,
ldmatrix_geom_m16n16_ops,
ldmatrix_geom_m8n16_ops);
list<WMMA_REGS> all_stmatrix_ops = !listconcat(stmatrix_b16_ops,
stmatrix_b8_ops);
}
def NVVM_MMA_OPS : NVVM_MMA_OPS;
// Returns true if this combination of fragment and layout for WMMA load/store
// ops is supported; false otherwise.
// E.g.
// if NVVM_WMMA_LDST_SUPPORTED<...>.ret then
// def : FOO<>; // The record will only be defined for supported ops.
//
class NVVM_WMMA_LDST_SUPPORTED<WMMA_REGS frag, string layout> {
string f = frag.frag;
string t = frag.ptx_elt_type;
bit ret = !cond(
// Sub-int load and store requires A fragment to be of row layout and B
// fragments to be of column layout.
!and(!or(!eq(t, "b1"),
!eq(t, "u4"),
!eq(t, "s4")),
!or(!and(!eq(f, "a"),
!ne(layout, "row")),
!and(!eq(f, "b"),
!ne(layout, "col")))) : false,
true: true
);
}
// Returns true if this combination of layout/satf/rnd for WMMA ops is
// supported; false otherwise.
// E.g.
// if NVVM_WMMA_SUPPORTED<...>.ret then
// def : FOO<>; // The record will only be defined for supported ops.
//
class NVVM_WMMA_SUPPORTED<list<WMMA_REGS> frags, string layout_a, string layout_b, int satf, string rnd> {
// WMMA ops check both layouts.
string layout = layout_a # ":" # layout_b;
string t = frags[0].ptx_elt_type;
bit ret = !cond(
// only f64 wmma functions support rnd options
// any non f64 type that uses a rnd value is invalid
!and(!ne(t, "f64"), !ne(rnd, "")) : false,
// satf is only valid for select types
!and(!eq(satf, 1),
!ne(t, "s8"),
!ne(t, "u8"),
!ne(t, "s4"),
!ne(t, "u4"),
!ne(t, "f16")): false,
// Sub-int wmma requires row/column layout
!and(!or(!eq(t, "s4"),
!eq(t, "u4"),
!eq(t, "b1")),
!ne(layout, "row:col")) : false,
true: true
);
}
class NVVM_MMA_B1OPS<list<WMMA_REGS> frags> {
list<string> ret = !cond(
!eq(frags[0].ptx_elt_type, "b1") : [".xor.popc", ".and.popc"],
true: [""]
);
}
// Returns true if this combination of layout/satf for MMA ops is supported;
// false otherwise.
// E.g.
// if NVVM_MMA_SUPPORTED<...>.ret then
// def : FOO<>; // The record will only be defined for supported ops.
//
class NVVM_MMA_SUPPORTED<list<WMMA_REGS> frags, string layout_a, string layout_b,
string kind, int satf> {
// MMA ops check both layouts.
string layout = layout_a # ":" # layout_b;
string a_type = frags[0].ptx_elt_type;
string b_type = frags[1].ptx_elt_type;
string c_type = frags[2].ptx_elt_type;
string d_type = frags[3].ptx_elt_type;
string geom = frags[0].geom;
// gcd is a shortcut used to identify instructions that depend on
// geom+frag_c+frag_d.
string gcd = geom # ":" # c_type # d_type;
bit ret = !cond(
// Limit satf to valid types
!and(!eq(satf, 1),
!ne(a_type, "s8"),
!ne(a_type, "u8"),
!ne(a_type, "s4"),
!ne(a_type, "u4")): false,
// m8n8k4 has no C=f32 D=f16 variant.
!eq(gcd, "m8n8k4:f32f16"): false,
// only m8n8k4 for f16 does not require row:col layout
!and(!ne(layout, "row:col"),
!or(!ne(geom, "m8n8k4"),
!ne(a_type, "f16"))) : false,
// m16n8k8 requires A and B to be the same type and C and D to be the same
// type.
!and(!eq(geom, "m16n8k8"),
!or(!ne(a_type, b_type),
!ne(c_type, d_type))): false,
// m16n8k16/m16n8k32 requires C and D to be the same type
!and(!or(!eq(geom, "m16n8k16"),
!eq(geom, "m16n8k32")),
!ne(c_type, d_type)): false,
// Limit kind to valid types and geometries
!and(!ne(kind, ""),
!or(!ne(geom, "m16n8k32"),
!and(!ne(a_type, "e4m3"),
!ne(a_type, "e5m2"),
!ne(a_type, "e3m2"),
!ne(a_type, "e2m3"),
!ne(a_type, "e2m1")))): false,
// Limit m16n8k16/m16n8k32 with no kind to valid types
!and(!eq(kind, ""),
!or(!eq(geom, "m16n8k16"),
!eq(geom, "m16n8k32")),
!or(!eq(a_type, "e3m2"),
!eq(a_type, "e2m3"),
!eq(a_type, "e2m1"),
!eq(b_type, "e3m2"),
!eq(b_type, "e2m3"),
!eq(b_type, "e2m1"))): false,
// All other are OK.
true: true
);
}
class NVVM_MMA_BLOCK_SCALE_SUPPORTED<list<WMMA_REGS> frags, string kind, string stype, string scale_vec_size> {
string geom = frags[0].geom;
bit ret = !cond(
!and(!eq(geom, "m16n8k64"),
!eq(kind, "mxf4"),
!or(!eq(scale_vec_size, ""),
!eq(scale_vec_size, ".scale_2x")),
!eq(stype, "ue8m0")) : true,
!and(!eq(geom, "m16n8k64"),
!eq(kind, "mxf4nvf4"),
!eq(scale_vec_size, ".scale_2x"),
!eq(stype, "ue8m0")) : true,
!and(!eq(geom, "m16n8k64"),
!eq(kind, "mxf4nvf4"),
!eq(scale_vec_size, ".scale_4x"),
!or(!eq(stype, "ue4m3"),
!eq(stype, "ue8m0"))) : true,
!and(!eq(geom, "m16n8k32"),
!eq(kind, "mxf8f6f4"),
!or(!eq(scale_vec_size, ""),
!eq(scale_vec_size, ".scale_1x")),
!eq(stype, "ue8m0")) : true,
true: false
);
}
// Returns true if the fragment is valid for ldmatrix ops is supported;
// false otherwise.
// E.g.
// if NVVM_LDMATRIX_SUPPORTED<...>.ret then
// def : FOO<>; // The record will only be defined for supported ops.
//
class NVVM_LDMATRIX_SUPPORTED<WMMA_REGS frag, bit trans> {
string g = frag.geom;
string t = frag.ptx_elt_type;
bit ret = !cond(
!and(!eq(g, "m8n8"), !eq(t, "b16")): true,
!and(!eq(g, "m16n16"), !eq(t, "b8"), !eq(trans, 1)): true,
!and(!eq(g, "m16n16"), !eq(t, "b8x16.b6x16_p32"), !eq(trans, 1)): true,
!and(!eq(g, "m16n16"), !eq(t, "b8x16.b4x16_p64"), !eq(trans, 1)): true,
!and(!eq(g, "m8n16"), !eq(t, "b8"), !eq(trans, 0)): true,
!and(!eq(g, "m8n16"), !eq(t, "b8x16.b6x16_p32"), !eq(trans, 0)): true,
!and(!eq(g, "m8n16"), !eq(t, "b8x16.b4x16_p64"), !eq(trans, 0)): true,
true: false
);
}
// Returns true if the fragment is valid for stmatrix ops is supported;
// false otherwise.
class NVVM_STMATRIX_SUPPORTED<WMMA_REGS frag, bit trans> {
string g = frag.geom;
string t = frag.ptx_elt_type;
bit ret = !cond(
!and(!eq(g, "m8n8"), !eq(t, "b16")): true,
!and(!eq(g, "m16n8"), !eq(t, "b8"), !eq(trans, 1)): true,
true: false
);
}
// Returns true if this combination of layout/kind/satf for MMA.SP ops is supported;
// false otherwise.
// E.g.
// if NVVM_MMA_SP_SUPPORTED<...>.ret then
// def : FOO<>; // The record will only be defined for supported ops.
//
class NVVM_MMA_SP_SUPPORTED<list<WMMA_REGS> frags, string metadata,
string kind, int satf> {
// MMA.SP ops check both layouts.
string a_type = frags[0].ptx_elt_type;
string b_type = frags[1].ptx_elt_type;
string c_type = frags[2].ptx_elt_type;
string d_type = frags[3].ptx_elt_type;
string geom = frags[0].geom;
bit is_int = !or(!eq(a_type, "s8"),
!eq(a_type, "u8"),
!eq(a_type, "s4"),
!eq(a_type, "u4"));
bit ret = !cond(
// Limit satf to valid types
!and(!eq(satf, 1),
!eq(is_int, 0)): false,
// f16/bf16/tf32 requires A and B to be the same type.
!and(!or(!eq(a_type, "f16"),
!eq(a_type, "bf16"),
!eq(a_type, "tf32")),
!ne(a_type, b_type)): false,
// m16n8k16, m16n8k32 and m16n8k64 requires C and D to be the same type.
!and(!or(!eq(geom, "m16n8k16"),
!eq(geom, "m16n8k32"),
!eq(geom, "m16n8k64")),
!ne(c_type, d_type)): false,
!and(!eq(kind, ""),
!or(!eq(a_type, "e3m2"),
!eq(a_type, "e2m3"),
!eq(a_type, "e2m1"),
!eq(b_type, "e3m2"),
!eq(b_type, "e2m3"),
!eq(b_type, "e2m1"))): false,
!and(!eq(kind, ""),
!eq(geom, "m16n8k64"),
!or(!eq(c_type, "f16"),
!eq(d_type, "f16"))): false,
!and(!ne(kind, ""),
!or(!eq(metadata, "sp"),
!ne(geom, "m16n8k64"),
!eq(is_int, 1))): false,
// All other are OK.
true: true
);
}
// Returns true if this combination of kind/scale_vec_size/stype
// for MMA.SP ops is supported;
// false otherwise.
// E.g.
// if NVVM_MMA_SP_BLOCK_SCALE_SUPPORTED<...>.ret then
// def : FOO<>; // The record will only be defined for supported ops.
//
class NVVM_MMA_SP_BLOCK_SCALE_SUPPORTED<list<WMMA_REGS> frags, string kind,
string stype, string scale_vec_size> {
// MMA.SP ops check both layouts.
string a_type = frags[0].ptx_elt_type;
string b_type = frags[1].ptx_elt_type;
string c_type = frags[2].ptx_elt_type;
string d_type = frags[3].ptx_elt_type;
string geom = frags[0].geom;
bit ret = !cond(
!and(!eq(geom, "m16n8k128"),
!eq(kind, "mxf4"),
!eq(stype, "ue8m0"),
!or(!eq(scale_vec_size, ""),
!eq(scale_vec_size, ".scale_2x"))): true,
!and(!eq(geom, "m16n8k128"),
!eq(kind, "mxf4nvf4"),
!eq(stype, "ue8m0"),
!eq(scale_vec_size, ".scale_2x")): true,
!and(!eq(geom, "m16n8k128"),
!eq(kind, "mxf4nvf4"),
!or(!eq(stype, "ue4m3"),
!eq(stype, "ue8m0")),
!eq(scale_vec_size, ".scale_4x")): true,
!and(!eq(geom, "m16n8k64"),
!eq(kind, "mxf8f6f4"),
!eq(stype, "ue8m0"),
!or(!eq(scale_vec_size, ""),
!eq(scale_vec_size, ".scale_1x"))): true,
// All other are NOT OK.
true: false
);
}
class SHFL_INFO<bit sync, string mode, string type, bit return_pred> {
string Suffix = !if(sync, "sync_", "")
# mode # "_"
# type
# !if(return_pred, "p", "");
string Name = "int_nvvm_shfl_" # Suffix;
bit withGccBuiltin = !not(return_pred);
LLVMType OpType = !cond(
!eq(type,"i32"): llvm_i32_ty,
!eq(type,"f32"): llvm_float_ty);
list<LLVMType> RetTy = !if(return_pred, [OpType, llvm_i1_ty], [OpType]);
list<LLVMType> ArgsTy = !if(sync,
[llvm_i32_ty, OpType, llvm_i32_ty, llvm_i32_ty],
[OpType, llvm_i32_ty, llvm_i32_ty]);
}
class NVVM_TCGEN05_LDST_ACCESS_SIZE<string Shape, int Num, string ElemType = "i32"> {
int shift = !cond(!eq(Shape, "16x128b"): 1,
!eq(Shape, "16x256b"): 2,
true : 0);
int veclen = !shl(1, !add(Num, shift));
int valid = !le(veclen, 128);
LLVMType type = !cond(!eq(veclen, 1): LLVMType<!cast<ValueType>(ElemType)>,
!eq(veclen, 2): LLVMType<!cast<ValueType>("v"#2#ElemType)>,
!eq(veclen, 4): LLVMType<!cast<ValueType>("v"#4#ElemType)>,
!eq(veclen, 8): LLVMType<!cast<ValueType>("v"#8#ElemType)>,
!eq(veclen, 16): LLVMType<!cast<ValueType>("v"#16#ElemType)>,
!eq(veclen, 32): LLVMType<!cast<ValueType>("v"#32#ElemType)>,
!eq(veclen, 64): LLVMType<!cast<ValueType>("v"#64#ElemType)>,
!eq(veclen, 128): LLVMType<!cast<ValueType>("v"#128#ElemType)>,
true : llvm_void_ty);
}
class NVVM_TCGEN05_MMA_BASE<string Space, bit IsSparse, string Kind = ""> {
LLVMType a_operand_type = !if(!eq(Space, "tensor"),
llvm_tmem_ptr_ty, llvm_i64_ty);
list<LLVMType> common_args = !listconcat(
[llvm_tmem_ptr_ty, // d
a_operand_type, // a
llvm_i64_ty, // b
llvm_i32_ty, // idesc
llvm_i1_ty], // enable_input_d
!if(!eq(IsSparse, 1), [llvm_tmem_ptr_ty], [])); // spmetadata
list<IntrinsicProperty> common_intr_props = !listconcat(
[IntrArgMemOnly, WriteOnly<ArgIndex<0>>],
!if(!eq(Space, "tensor"), [ReadOnly<ArgIndex<1>>], [])
);
string Prefix = "llvm.nvvm.tcgen05.mma";
string SpSpaceKindStr = !if(IsSparse, ".sp", "")
# "." # Space
# !if(!ne(Kind, ""), "." # Kind, "");
}
class NVVM_TCGEN05_MMA<bit IsSparse, string Space,
bit IsAShift, bit IsScaleInputD>:
NVVM_TCGEN05_MMA_BASE<Space, IsSparse> {
string name = Prefix
# SpSpaceKindStr
# !if(IsScaleInputD, ".scale_d", "")
# !if(IsAShift, ".ashift", "");
string intr_name = IntrinsicName<name>.intr_name;
string record_name = IntrinsicName<name>.record_name;
}
class NVVM_TCGEN05_MMA_BLOCKSCALE<bit IsSparse, string Space,
string Kind, string ScaleVecSize>:
NVVM_TCGEN05_MMA_BASE<Space, IsSparse, Kind> {
string name = Prefix
# SpSpaceKindStr
# ".block_scale" # ScaleVecSize;
string intr_name = IntrinsicName<name>.intr_name;
string record_name = IntrinsicName<name>.record_name;
}
class NVVM_TCGEN05_MMA_WS<bit IsSparse, string Space, bit IsZeroColMask>:
NVVM_TCGEN05_MMA_BASE<Space, IsSparse> {
string name = Prefix # ".ws"
# SpSpaceKindStr
# !if(IsZeroColMask, ".zero_col_mask", "");
string intr_name = IntrinsicName<name>.intr_name;
string record_name = IntrinsicName<name>.record_name;
}
class NVVM_TCGEN05_MMA_DISABLE_OUTPUT_LANE<bit IsSparse, string Space,
int CtaGroup, bit IsAShift,
bit IsScaleInputD>:
NVVM_TCGEN05_MMA_BASE<Space, IsSparse> {
string name = Prefix
# SpSpaceKindStr
# !if(IsScaleInputD, ".scale_d", "")
# ".disable_output_lane.cg" # CtaGroup
# !if(IsAShift, ".ashift", "");
string intr_name = IntrinsicName<name>.intr_name;
string record_name = IntrinsicName<name>.record_name;
}
class NVVM_TCGEN05_MMA_BLOCKSCALE_SUPPORTED<string Kind, string ScaleVecSize> {
bit ret = !cond(
!and(!eq(Kind, "mxf8f6f4"), !eq(ScaleVecSize, "")) : true,
!and(!eq(Kind, "mxf4"), !eq(ScaleVecSize, "")) : true,
!and(!eq(Kind, "mxf4nvf4"), !eq(ScaleVecSize, ".block16")) : true,
!and(!eq(Kind, "mxf4"), !eq(ScaleVecSize, ".block32")) : true,
!and(!eq(Kind, "mxf4nvf4"), !eq(ScaleVecSize, ".block32")) : true,
!and(!eq(Kind, "mxf8f6f4"), !eq(ScaleVecSize, ".block32")) : true,
true: false
);
}
class TexVector<string name, list<LLVMType> types> {
string Name = name;
list<LLVMType> Types = types;
}
def TV_I8 : TexVector<"i8", [llvm_i16_ty]>;
def TV_I16 : TexVector<"i16", [llvm_i16_ty]>;
def TV_I32 : TexVector<"i32", [llvm_i32_ty]>;
def TV_I64 : TexVector<"i64", [llvm_i64_ty]>;
def TV_V2I8 : TexVector<"v2i8", !listsplat(llvm_i16_ty, 2)>;
def TV_V2I16 : TexVector<"v2i16", !listsplat(llvm_i16_ty, 2)>;
def TV_V2I32 : TexVector<"v2i32", !listsplat(llvm_i32_ty, 2)>;
def TV_V2I64 : TexVector<"v2i64", !listsplat(llvm_i64_ty, 2)>;
def TV_V4I8 : TexVector<"v4i8", !listsplat(llvm_i16_ty, 4)>;
def TV_V4I16 : TexVector<"v4i16", !listsplat(llvm_i16_ty, 4)>;
def TV_V4I32 : TexVector<"v4i32", !listsplat(llvm_i32_ty, 4)>;
def V4F32 : TexVector<"v4f32", !listsplat(llvm_float_ty, 4)>;
def V4S32 : TexVector<"v4s32", !listsplat(llvm_i32_ty, 4)>;
def V4U32 : TexVector<"v4u32", !listsplat(llvm_i32_ty, 4)>;
class NVVMBuiltin :
ClangBuiltin<!strconcat("__", !substr(NAME, !size("int_")))> {
assert !eq(!substr(NAME, 0, !size("int_nvvm_")), "int_nvvm_"),
"NVVMBuiltin must be a NVVM intrinsic starting with 'int_nvvm_'";
}
class NVVMPureIntrinsic<list<LLVMType> ret_types,
list<LLVMType> param_types = [],
list<IntrinsicProperty> intr_properties = [],
string name = ""> :
DefaultAttrsIntrinsic<ret_types, param_types,
intr_properties # [IntrNoMem, IntrSpeculatable], name> {}
let TargetPrefix = "nvvm" in {
//
// PRMT - permute
//
def int_nvvm_prmt : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
foreach mode = ["f4e", "b4e"] in
def int_nvvm_prmt_ # mode :
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
// Note: these variants also have 2 source operands but only one will ever
// be used so we eliminate the other operand in the IR (0 is used as the
// placeholder in the backend).
foreach mode = ["rc8", "ecl", "ecr", "rc16"] in
def int_nvvm_prmt_ # mode :
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
//
// Nanosleep
//
def int_nvvm_nanosleep : NVVMBuiltin,
DefaultAttrsIntrinsic<[], [llvm_i32_ty],
[IntrConvergent, IntrNoMem, IntrHasSideEffects]>;
//
// Performance Monitor Events (pm events) intrinsics
//
def int_nvvm_pm_event_mask : NVVMBuiltin,
DefaultAttrsIntrinsic<[], [llvm_i16_ty],
[IntrConvergent, IntrNoMem, IntrHasSideEffects,
ImmArg<ArgIndex<0>>]>;
//
// Min Max
//
let IntrProperties = [IntrNoMem, IntrSpeculatable, Commutative,
IntrNoCreateUndefOrPoison] in {
foreach operation = ["min", "max"] in {
def int_nvvm_f # operation # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty, llvm_double_ty]>;
foreach variant = ["", "_xorsign_abs"] in {
foreach nan = ["", "_nan"] in {
foreach ftz = ["", "_ftz"] in {
def int_nvvm_f # operation # ftz # nan # variant # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_f # operation # ftz # nan # variant # _f16 :
DefaultAttrsIntrinsic<[llvm_half_ty], [llvm_half_ty, llvm_half_ty]>;
def int_nvvm_f # operation # ftz # nan # variant # _f16x2 :
DefaultAttrsIntrinsic<[llvm_v2f16_ty], [llvm_v2f16_ty, llvm_v2f16_ty]>;
def int_nvvm_f # operation # ftz # nan # variant # _bf16 : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_bfloat_ty], [llvm_bfloat_ty, llvm_bfloat_ty]>;
def int_nvvm_f # operation # ftz # nan # variant # _bf16x2 : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_v2bf16_ty], [llvm_v2bf16_ty, llvm_v2bf16_ty]>;
} // ftz
} // nan
} // variant
} // operation
}
//
// Multiplication
//
let IntrProperties = [IntrNoMem, IntrSpeculatable, Commutative,
IntrNoCreateUndefOrPoison] in {
foreach sign = ["", "u"] in {
def int_nvvm_mulhi_ # sign # s : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i16_ty], [llvm_i16_ty, llvm_i16_ty]>;
def int_nvvm_mulhi_ # sign # i : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_mulhi_ # sign # ll : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty]>;
def int_nvvm_mul24_ # sign # i : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
}
foreach rnd = ["rn", "rz", "rm", "rp"] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_mul_ # rnd # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_mul_ # rnd # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty, llvm_double_ty]>;
}
foreach ftz = ["", "_ftz"] in {
def int_nvvm_mul_rn # ftz # _sat_f16 : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_half_ty], [llvm_half_ty, llvm_half_ty]>;
def int_nvvm_mul_rn # ftz # _sat_v2f16 : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_v2f16_ty], [llvm_v2f16_ty, llvm_v2f16_ty]>;
} // ftz
}
//
// Div
//
let IntrProperties = [IntrNoMem, IntrNoCreateUndefOrPoison] in {
foreach ftz = ["", "_ftz"] in {
def int_nvvm_div_approx # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_div_full # ftz : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty]>;
}
foreach rnd = ["rn", "rz", "rm", "rp"] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_div_ # rnd # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_div_ # rnd # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty, llvm_double_ty]>;
}
}
//
// Sad - Sum of Absolute Differences
//
foreach sign = ["", "u"] in {
def int_nvvm_sad_ # sign # s : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_i16_ty, llvm_i16_ty, llvm_i16_ty]>;
def int_nvvm_sad_ # sign # i : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_sad_ # sign # ll : NVVMBuiltin,
PureIntrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty, llvm_i64_ty]>;
}
//
// Floor Ceil
//
foreach op = ["floor", "ceil"] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_ # op # ftz # _f : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_ # op # _d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
}
//
// Abs
//
foreach ftz = ["", "_ftz"] in
def int_nvvm_fabs # ftz :
PureIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
//
// Neg bf16, bf16x2
//
def int_nvvm_neg_bf16 : NVVMBuiltin,
PureIntrinsic<[llvm_bfloat_ty], [llvm_bfloat_ty]>;
def int_nvvm_neg_bf16x2 : NVVMBuiltin,
PureIntrinsic<[llvm_v2bf16_ty], [llvm_v2bf16_ty]>;
//
// Round
//
foreach ftz = ["", "_ftz"] in
def int_nvvm_round # ftz # _f : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_round_d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
//
// Trunc
//
foreach ftz = ["", "_ftz"] in
def int_nvvm_trunc # ftz # _f : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_trunc_d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
//
// Saturate
//
foreach ftz = ["", "_ftz"] in
def int_nvvm_saturate # ftz # _f : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_saturate_d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
//
// Exp2 Log2
//
let IntrProperties = [IntrNoMem] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_ex2_approx # ftz :
PureIntrinsic<[llvm_anyfloat_ty], [LLVMMatchType<0>]>;
foreach ftz = ["", "_ftz"] in
def int_nvvm_lg2_approx # ftz # _f : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_lg2_approx_d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
}
//
// Sin Cos
//
foreach op = ["sin", "cos"] in
foreach ftz = ["", "_ftz"] in
def int_nvvm_ # op # _approx # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty],
[IntrNoMem, IntrNoCreateUndefOrPoison]>;
//
// Fma
//
foreach variant = ["", "_sat", "_relu"] in {
foreach ftz = ["", "_ftz"] in {
def int_nvvm_fma_rn # ftz # variant # _f16 :
PureIntrinsic<[llvm_half_ty],
[llvm_half_ty, llvm_half_ty, llvm_half_ty]>;
def int_nvvm_fma_rn # ftz # variant # _f16x2 :
PureIntrinsic<[llvm_v2f16_ty],
[llvm_v2f16_ty, llvm_v2f16_ty, llvm_v2f16_ty]>;
} // ftz
} // variant
foreach relu = ["", "_relu"] in {
def int_nvvm_fma_rn # relu # _bf16 : NVVMBuiltin,
PureIntrinsic<[llvm_bfloat_ty],
[llvm_bfloat_ty, llvm_bfloat_ty, llvm_bfloat_ty]>;
def int_nvvm_fma_rn # relu # _bf16x2 : NVVMBuiltin,
PureIntrinsic<[llvm_v2bf16_ty],
[llvm_v2bf16_ty, llvm_v2bf16_ty, llvm_v2bf16_ty]>;
// oob (out-of-bounds) - clamps the result to 0 if either of the operand is
// an OOB NaN value.
def int_nvvm_fma_rn_oob # relu : PureIntrinsic<[llvm_anyfloat_ty],
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
} // relu
foreach rnd = ["_rn", "_rz", "_rm", "_rp"] in {
foreach ftz = ["", "_ftz"] in {
foreach sat = ["", "_sat"] in {
def int_nvvm_fma # rnd # ftz # sat # _f : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty],
[llvm_float_ty, llvm_float_ty, llvm_float_ty]>;
} // sat
} // ftz
def int_nvvm_fma # rnd # _d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty],
[llvm_double_ty, llvm_double_ty, llvm_double_ty]>;
} // rnd
//
// Rcp
//
let IntrProperties = [IntrNoMem, IntrNoCreateUndefOrPoison] in {
foreach rnd = ["rn", "rz", "rm", "rp"] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_rcp_ # rnd # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_rcp_ # rnd # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
}
def int_nvvm_rcp_approx_ftz_f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_rcp_approx_ftz_d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
}
//
// Sqrt
//
let IntrProperties = [IntrNoMem, IntrNoCreateUndefOrPoison] in {
foreach rnd = ["rn", "rz", "rm", "rp"] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_sqrt_ # rnd # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_sqrt_ # rnd # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
}
def int_nvvm_sqrt_f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
foreach ftz = ["", "_ftz"] in
def int_nvvm_sqrt_approx # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
}
//
// Rsqrt
//
let IntrProperties = [IntrNoMem, IntrNoCreateUndefOrPoison] in {
foreach ftz = ["", "_ftz"] in {
def int_nvvm_rsqrt_approx # ftz # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_rsqrt_approx # ftz # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
}
}
//
// Add
//
let IntrProperties = [IntrNoMem, IntrSpeculatable, Commutative,
IntrNoCreateUndefOrPoison] in {
foreach rnd = ["_rn", "_rz", "_rm", "_rp"] in {
foreach ftz = ["", "_ftz"] in {
foreach sat = ["", "_sat"] in {
def int_nvvm_add # rnd # ftz # sat # _f : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty, llvm_float_ty]>;
} // sat
} // ftz
def int_nvvm_add # rnd # _d : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty, llvm_double_ty]>;
}
foreach ftz = ["", "_ftz"] in {
def int_nvvm_add_rn # ftz # _sat_f16 : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_half_ty], [llvm_half_ty, llvm_half_ty]>;
def int_nvvm_add_rn # ftz # _sat_v2f16 : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_v2f16_ty], [llvm_v2f16_ty, llvm_v2f16_ty]>;
} // ftz
}
//
// Dot Product
//
foreach a_type = ["s", "u"] in {
foreach b_type = ["s", "u"] in {
def int_nvvm_idp4a_ # a_type # _ # b_type :
PureIntrinsic<[llvm_i32_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_idp2a_ # a_type # _ # b_type :
PureIntrinsic<[llvm_i32_ty],
[llvm_i32_ty, llvm_i32_ty, llvm_i1_ty, llvm_i32_ty],
[ImmArg<ArgIndex<2>>]>;
}
}
//
// Funnel-shift
//
foreach direction = ["l", "r"] in
def int_nvvm_fsh # direction # _clamp :
PureIntrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>, LLVMMatchType<0>, LLVMMatchType<0>]>;
//
// FLO - Find Leading One
//
foreach sign = ["s", "u"] in
def int_nvvm_flo_ # sign :
PureIntrinsic<[llvm_i32_ty], [llvm_anyint_ty, llvm_i1_ty],
[ImmArg<ArgIndex<1>>]>;
//
// szext
//
foreach ext = ["sext", "zext"] in
foreach mode = ["wrap", "clamp"] in
def int_nvvm_ # ext # _ # mode :
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
//
// BMSK - bit mask
//
foreach mode = ["wrap", "clamp"] in
def int_nvvm_bmsk_ # mode :
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
//
// FNS - Find the n-th set bit
//
def int_nvvm_fns : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
//
// Convert
//
// TODO: All these intrinsics are defined as NVVMPureIntrinsic, this attaches the
// IntrSpeculatable property to them. Consider if some of these should
// have this attribute removed as they may be too expensive.
//
def int_nvvm_lohi_i2d : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_d2i_lo : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_double_ty]>;
def int_nvvm_d2i_hi : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_double_ty]>;
foreach rnd = ["rn", "rz", "rm", "rp"] in {
foreach ftz = ["", "_ftz"] in
def int_nvvm_d2f_ # rnd # ftz : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_double_ty]>;
foreach sign = ["", "u"] in {
def int_nvvm_d2 # sign # i_ # rnd : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_double_ty]>;
def int_nvvm_ # sign # i2d_ # rnd : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_i32_ty]>;
foreach ftz = ["", "_ftz"] in
def int_nvvm_f2 # sign # i_ # rnd # ftz : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_float_ty]>;
def int_nvvm_ # sign # i2f_ # rnd : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_i32_ty]>;
foreach ftz = ["", "_ftz"] in
def int_nvvm_f2 # sign # ll_ # rnd # ftz : NVVMBuiltin,
PureIntrinsic<[llvm_i64_ty], [llvm_float_ty]>;
def int_nvvm_d2 # sign # ll_ # rnd : NVVMBuiltin,
PureIntrinsic<[llvm_i64_ty], [llvm_double_ty]>;
def int_nvvm_ # sign # ll2f_ # rnd : NVVMBuiltin,
PureIntrinsic<[llvm_float_ty], [llvm_i64_ty]>;
def int_nvvm_ # sign # ll2d_ # rnd : NVVMBuiltin,
PureIntrinsic<[llvm_double_ty], [llvm_i64_ty]>;
} // sign
} // rnd
foreach ftz = ["", "_ftz"] in {
def int_nvvm_f2h_rn # ftz : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_float_ty]>;
def int_nvvm_bf2h_rn # ftz : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_bfloat_ty]>;
}
foreach rnd = ["rn", "rz"] in {
foreach relu = ["", "_relu"] in {
foreach satfinite = ["", "_satfinite"] in {
def int_nvvm_ff2bf16x2_ # rnd # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_v2bf16_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_ff2f16x2_ # rnd # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_v2f16_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_f2bf16_ # rnd # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_bfloat_ty], [llvm_float_ty]>;
def int_nvvm_f2f16_ # rnd # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_half_ty], [llvm_float_ty]>;
}
}
}
// RS rounding mode (Stochastic Rounding) conversions for f16x2, bf16x2 types
// The last i32 operand provides the random bits for the conversion
foreach relu = ["", "_relu"] in {
foreach satfinite = ["", "_satfinite"] in {
def int_nvvm_ff2f16x2_rs # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_v2f16_ty], [llvm_float_ty, llvm_float_ty, llvm_i32_ty]>;
def int_nvvm_ff2bf16x2_rs # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_v2bf16_ty], [llvm_float_ty, llvm_float_ty, llvm_i32_ty]>;
}
}
foreach satfinite = ["", "_satfinite"] in {
def int_nvvm_f2tf32_rna # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_float_ty]>;
foreach rnd = ["rn", "rz"] in
foreach relu = ["", "_relu"] in
def int_nvvm_f2tf32_ # rnd # relu # satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_i32_ty], [llvm_float_ty]>;
}
foreach relu = ["", "_relu"] in {
def int_nvvm_ff_to_s2f6x2_rn # relu # _satfinite_scale_n2_ue8m0 :
PureIntrinsic<[llvm_i16_ty], [llvm_float_ty, llvm_float_ty, llvm_i16_ty]>;
def int_nvvm_bf16x2_to_s2f6x2_rn # relu # _satfinite_scale_n2_ue8m0 :
PureIntrinsic<[llvm_i16_ty], [llvm_v2bf16_ty, llvm_i16_ty]>;
def int_nvvm_s2f6x2_to_bf16x2_rn # relu # _satfinite_scale_n2_ue8m0 :
PureIntrinsic<[llvm_v2bf16_ty], [llvm_i16_ty, llvm_i16_ty]>;
// No satfinite variants
def int_nvvm_s2f6x2_to_bf16x2_rn # relu # _scale_n2_ue8m0 :
PureIntrinsic<[llvm_v2bf16_ty], [llvm_i16_ty, llvm_i16_ty]>;
}
foreach type = ["e4m3x2", "e5m2x2"] in {
foreach relu = ["", "_relu"] in {
def int_nvvm_ff_to_ # type # _rn # relu : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_f16x2_to_ # type # _rn # relu : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_v2f16_ty]>;
def int_nvvm_ # type # _to_f16x2_rn # relu : NVVMBuiltin,
PureIntrinsic<[llvm_v2f16_ty], [llvm_i16_ty]>;
def int_nvvm_bf16x2_to_ # type # _rn # relu # _satfinite
: PureIntrinsic<[llvm_i16_ty], [llvm_v2bf16_ty]>;
foreach satfinite = ["", "_satfinite"] in {
def int_nvvm_ # type # _to_bf16x2_rn # relu # satfinite # _scale_n2_ue8m0
: PureIntrinsic<[llvm_v2bf16_ty], [llvm_i16_ty, llvm_i16_ty]>;
}
}
}
// RS rounding mode (Stochastic Rounding) conversions for f8x4 types
// The last i32 operand provides the random bits for the conversion
foreach type = ["e4m3x4", "e5m2x4"] in {
foreach relu = ["", "_relu"] in {
def int_nvvm_f32x4_to_ # type # _rs # relu # _satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_v4i8_ty], [llvm_v4f32_ty, llvm_i32_ty]>;
}
}
// FP4 conversions.
foreach relu = ["", "_relu"] in {
def int_nvvm_ff_to_e2m1x2_rn # relu # _satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_e2m1x2_to_f16x2_rn # relu : NVVMBuiltin,
PureIntrinsic<[llvm_v2f16_ty], [llvm_i16_ty]>;
def int_nvvm_f16x2_to_e2m1x2_rn # relu # _satfinite
: PureIntrinsic<[llvm_i16_ty], [llvm_v2f16_ty]>;
def int_nvvm_bf16x2_to_e2m1x2_rn # relu # _satfinite
: PureIntrinsic<[llvm_i16_ty], [llvm_v2bf16_ty]>;
foreach satfinite = ["", "_satfinite"] in {
def int_nvvm_e2m1x2_to_bf16x2_rn # relu # satfinite # _scale_n2_ue8m0
: PureIntrinsic<[llvm_v2bf16_ty], [llvm_i16_ty, llvm_i16_ty]>;
}
}
// RS rounding mode (Stochastic Rounding) conversions for f4x4 type
// The last i32 operand provides the random bits for the conversion
foreach relu = ["", "_relu"] in {
def int_nvvm_f32x4_to_e2m1x4_rs # relu # _satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_v4f32_ty, llvm_i32_ty]>;
}
// FP6 conversions.
foreach type = ["e2m3x2", "e3m2x2"] in {
foreach relu = ["", "_relu"] in {
def int_nvvm_ff_to_ # type # _rn # relu # _satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_ # type # _to_f16x2_rn # relu : NVVMBuiltin,
PureIntrinsic<[llvm_v2f16_ty], [llvm_i16_ty]>;
def int_nvvm_f16x2_to_ # type # _rn # relu # _satfinite
: PureIntrinsic<[llvm_i16_ty], [llvm_v2f16_ty]>;
def int_nvvm_bf16x2_to_ # type # _rn # relu # _satfinite
: PureIntrinsic<[llvm_i16_ty], [llvm_v2bf16_ty]>;
foreach satfinite = ["", "_satfinite"] in {
def int_nvvm_ # type # _to_bf16x2_rn # relu # satfinite # _scale_n2_ue8m0
: PureIntrinsic<[llvm_v2bf16_ty], [llvm_i16_ty, llvm_i16_ty]>;
}
}
}
// RS rounding mode (Stochastic Rounding) conversions for f6x4 types
// The last i32 operand provides the random bits for the conversion
foreach type = ["e2m3x4", "e3m2x4"] in {
foreach relu = ["", "_relu"] in {
def int_nvvm_f32x4_to_ # type # _rs # relu # _satfinite : NVVMBuiltin,
PureIntrinsic<[llvm_v4i8_ty], [llvm_v4f32_ty, llvm_i32_ty]>;
}
}
// UE8M0x2 conversions.
foreach rmode = ["_rz", "_rp"] in {
foreach satmode = ["", "_satfinite"] in {
defvar suffix = rmode # satmode;
def int_nvvm_ff_to_ue8m0x2 # suffix : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_float_ty, llvm_float_ty]>;
def int_nvvm_bf16x2_to_ue8m0x2 # suffix : NVVMBuiltin,
PureIntrinsic<[llvm_i16_ty], [llvm_v2bf16_ty]>;
}
}
def int_nvvm_ue8m0x2_to_bf16x2 : NVVMBuiltin,
PureIntrinsic<[llvm_v2bf16_ty], [llvm_i16_ty]>;
//
// Bar.Sync
//
def int_nvvm_bar_warp_sync : NVVMBuiltin,
Intrinsic<[], [llvm_i32_ty], [IntrConvergent, IntrNoCallback]>;
multiclass BarrierCTAIntrinsic<list<LLVMType> ret_types = [],
list<LLVMType> extra_param_types = [],
bit has_all_variant = true> {
let IntrProperties = [IntrConvergent, IntrNoCallback] in {
foreach align = ["", "_aligned"] in {
def align # _count :
Intrinsic<ret_types, [llvm_i32_ty, llvm_i32_ty] # extra_param_types>;
if has_all_variant then
def align # _all :
Intrinsic<ret_types, [llvm_i32_ty] # extra_param_types>;
}
}
}
// barrier{.cta}.sync{.aligned} a{, b};
// barrier{.cta}.arrive{.aligned} a, b;
defm int_nvvm_barrier_cta_sync : BarrierCTAIntrinsic<>;
defm int_nvvm_barrier_cta_arrive : BarrierCTAIntrinsic<has_all_variant = false>;
// barrier{.cta}.red.popc{.aligned}.u32 d, a{, b}, {!}c;
// barrier{.cta}.red.op{.aligned}.pred p, a{, b}, {!}c;
// .op = { .and, .or };
defm int_nvvm_barrier_cta_red_popc : BarrierCTAIntrinsic<[llvm_i32_ty], [llvm_i1_ty]>;
defm int_nvvm_barrier_cta_red_and : BarrierCTAIntrinsic<[llvm_i1_ty], [llvm_i1_ty]>;
defm int_nvvm_barrier_cta_red_or : BarrierCTAIntrinsic<[llvm_i1_ty], [llvm_i1_ty]>;
let IntrProperties = [IntrConvergent, IntrNoCallback] in {
// barrier.cluster.[wait, arrive, arrive.relaxed]
def int_nvvm_barrier_cluster_arrive : Intrinsic<[]>;
def int_nvvm_barrier_cluster_arrive_relaxed : Intrinsic<[]>;
def int_nvvm_barrier_cluster_wait : Intrinsic<[]>;
// 'aligned' versions of the above barrier.cluster.* intrinsics
def int_nvvm_barrier_cluster_arrive_aligned : Intrinsic<[]>;
def int_nvvm_barrier_cluster_arrive_relaxed_aligned : Intrinsic<[]>;
def int_nvvm_barrier_cluster_wait_aligned : Intrinsic<[]>;
}
//
// Membar / Fence
//
let IntrProperties = [IntrNoCallback] in {
def int_nvvm_membar_cta : NVVMBuiltin, Intrinsic<[]>;
def int_nvvm_membar_gl : NVVMBuiltin, Intrinsic<[]>;
def int_nvvm_membar_sys : NVVMBuiltin, Intrinsic<[]>;
def int_nvvm_fence_sc_cluster : Intrinsic<[]>;
// Operation fence
def int_nvvm_fence_mbarrier_init_release_cluster: Intrinsic<[], [], [],
"llvm.nvvm.fence.mbarrier_init.release.cluster">;
// Thread fence
def int_nvvm_fence_acquire_sync_restrict_space_cluster_scope_cluster :
Intrinsic<[], [], [],
"llvm.nvvm.fence.acquire.sync_restrict.space.cluster.scope.cluster">;
def int_nvvm_fence_release_sync_restrict_space_cta_scope_cluster :
Intrinsic<[], [], [],
"llvm.nvvm.fence.release.sync_restrict.space.cta.scope.cluster">;
//
// Proxy fence (uni-directional)
//
def int_nvvm_fence_proxy_async_generic_acquire_sync_restrict_space_cluster_scope_cluster :
Intrinsic<[], [], [],
"llvm.nvvm.fence.proxy.async_generic.acquire.sync_restrict.space.cluster.scope.cluster">;
def int_nvvm_fence_proxy_async_generic_release_sync_restrict_space_cta_scope_cluster :
Intrinsic<[], [], [],
"llvm.nvvm.fence.proxy.async_generic.release.sync_restrict.space.cta.scope.cluster">;
foreach scope = ["cta", "cluster", "gpu", "sys"] in {
def int_nvvm_fence_proxy_tensormap_generic_release_ # scope :
Intrinsic<[], [], [],
"llvm.nvvm.fence.proxy.tensormap_generic.release." # scope>;
// The imm-arg 'size' can only be 128.
def int_nvvm_fence_proxy_tensormap_generic_acquire_ # scope :
Intrinsic<[], [llvm_ptr_ty, llvm_i32_ty], [],
"llvm.nvvm.fence.proxy.tensormap_generic.acquire." # scope> {
let IntrProperties = [IntrNoCallback, IntrArgMemOnly,
ImmArg<ArgIndex<1>>, Range<ArgIndex<1>, 128, 129>];
}
}
//
// Proxy fence (bi-directional)
//
foreach proxykind = ["alias", "async", "async.global", "async.shared_cta",
"async.shared_cluster"] in {
defvar Intr = IntrinsicName<"llvm.nvvm.fence.proxy." # proxykind>;
def Intr.record_name: Intrinsic<[], [], [], Intr.intr_name>;
}
}
//
// Async Copy
//
let IntrProperties = [IntrConvergent, IntrNoCallback] in {
def int_nvvm_cp_async_mbarrier_arrive : NVVMBuiltin,
Intrinsic<[], [llvm_ptr_ty]>;
def int_nvvm_cp_async_mbarrier_arrive_shared : NVVMBuiltin,
Intrinsic<[], [llvm_shared_ptr_ty]>;
def int_nvvm_cp_async_mbarrier_arrive_noinc : NVVMBuiltin,
Intrinsic<[], [llvm_ptr_ty]>;
def int_nvvm_cp_async_mbarrier_arrive_noinc_shared : NVVMBuiltin,
Intrinsic<[], [llvm_shared_ptr_ty]>;
}
multiclass CP_ASYNC_SHARED_GLOBAL {
let IntrProperties = [IntrArgMemOnly, IntrNoCallback, NoAlias<ArgIndex<0>>,
NoAlias<ArgIndex<1>>, WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<1>>] in {
def NAME : Intrinsic<[], [llvm_shared_ptr_ty, llvm_global_ptr_ty]>;
def _s : Intrinsic<[], [llvm_shared_ptr_ty, llvm_global_ptr_ty, llvm_i32_ty]>;
}
}
defm int_nvvm_cp_async_ca_shared_global_4 : CP_ASYNC_SHARED_GLOBAL;
defm int_nvvm_cp_async_ca_shared_global_8 : CP_ASYNC_SHARED_GLOBAL;
defm int_nvvm_cp_async_ca_shared_global_16 : CP_ASYNC_SHARED_GLOBAL;
defm int_nvvm_cp_async_cg_shared_global_16 : CP_ASYNC_SHARED_GLOBAL;
def int_nvvm_cp_async_commit_group : NVVMBuiltin, Intrinsic<[]>;
def int_nvvm_cp_async_wait_group : NVVMBuiltin,
Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
def int_nvvm_cp_async_wait_all : NVVMBuiltin, Intrinsic<[]>;
// cp.async.bulk variants of the commit/wait group
def int_nvvm_cp_async_bulk_commit_group : Intrinsic<[]>;
def int_nvvm_cp_async_bulk_wait_group :
Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
def int_nvvm_cp_async_bulk_wait_group_read :
Intrinsic<[], [llvm_i32_ty], [ImmArg<ArgIndex<0>>]>;
// mbarrier
foreach is_shared = [true, false] in {
defvar mbarrier_ptr_ty = !if(is_shared, llvm_shared_ptr_ty, llvm_ptr_ty);
defvar shared = !if(is_shared, "_shared", "");
def int_nvvm_mbarrier_init # shared : NVVMBuiltin,
Intrinsic<[], [mbarrier_ptr_ty, llvm_i32_ty],
[IntrConvergent, IntrNoCallback]>;
def int_nvvm_mbarrier_inval # shared : NVVMBuiltin,
Intrinsic<[], [mbarrier_ptr_ty],
[IntrConvergent, IntrWriteMem, IntrArgMemOnly, IntrNoCallback,
WriteOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>]>;
let IntrProperties = [IntrConvergent, IntrNoCallback] in {
foreach drop = ["", "_drop"] in {
def int_nvvm_mbarrier_arrive # drop # shared : NVVMBuiltin,
Intrinsic<[llvm_i64_ty], [mbarrier_ptr_ty]>;
def int_nvvm_mbarrier_arrive # drop # _noComplete # shared : NVVMBuiltin,
Intrinsic<[llvm_i64_ty], [mbarrier_ptr_ty, llvm_i32_ty]>;
}
def int_nvvm_mbarrier_test_wait # shared : NVVMBuiltin,
Intrinsic<[llvm_i1_ty], [mbarrier_ptr_ty, llvm_i64_ty]>;
}
}
def int_nvvm_mbarrier_pending_count : NVVMBuiltin,
NVVMPureIntrinsic<[llvm_i32_ty], [llvm_i64_ty]>;
// mbarrier.{expect_tx/complete_tx}
foreach op = ["expect_tx", "complete_tx"] in {
foreach scope = ["scope_cta", "scope_cluster"] in {
foreach space = ["space_cta", "space_cluster"] in {
defvar suffix = StrJoin<"_", [op, scope, space]>.ret;
defvar mbar_addr_ty = !if(!eq(space, "space_cta"),
llvm_shared_ptr_ty, llvm_shared_cluster_ptr_ty);
def int_nvvm_mbarrier_ # suffix :
Intrinsic<[], [mbar_addr_ty, llvm_i32_ty],
[IntrConvergent, IntrArgMemOnly, IntrNoCallback]>;
} // space
} // scope
} // op
// mbarrier.arrive and mbarrier.arrive.expect_tx
// mbarrier.arrive_drop and mbarrier.arrive_drop.expect_tx
foreach op = ["arrive", "arrive_expect_tx",
"arrive_drop", "arrive_drop_expect_tx"] in {
foreach scope = ["scope_cta", "scope_cluster"] in {
foreach space = ["space_cta", "space_cluster"] in {
defvar suffix = StrJoin<"_", [scope, space]>.ret;
defvar mbar_addr_ty = !if(!eq(space, "space_cta"),
llvm_shared_ptr_ty, llvm_shared_cluster_ptr_ty);
defvar args_ty = [mbar_addr_ty, // mbar_address_ptr
llvm_i32_ty]; // tx-count
// mbarriers in shared_cluster space cannot return any value.
defvar mbar_ret_ty = !if(!eq(space, "space_cta"),
[llvm_i64_ty], []<LLVMType>);
def int_nvvm_mbarrier_ # op # "_" # suffix:
Intrinsic<mbar_ret_ty, args_ty,
[IntrConvergent, IntrNoCallback]>;
def int_nvvm_mbarrier_ # op # "_relaxed_" # suffix :
Intrinsic<mbar_ret_ty, args_ty,
[IntrConvergent, IntrArgMemOnly, IntrNoCallback]>;
} // space
} // scope
} // op
// mbarrier.{test_wait and try_wait}
foreach op = ["test_wait", "try_wait"] in {
foreach scope = ["scope_cta", "scope_cluster"] in {
foreach parity = [true, false] in {
foreach time_limit = !if(!eq(op, "try_wait"), [true, false], [false]) in {
defvar base_args = [llvm_shared_ptr_ty]; // mbar_ptr
defvar parity_args = !if(parity, [llvm_i32_ty], [llvm_i64_ty]);
defvar tl_args = !if(time_limit, [llvm_i32_ty], []<LLVMType>);
defvar args = !listconcat(base_args, parity_args, tl_args);
defvar tmp_op = StrJoin<"_", [op,
!if(parity, "parity", ""),
!if(time_limit, "tl", "")]>.ret;
defvar suffix = StrJoin<"_", [scope, "space_cta"]>.ret;
def int_nvvm_mbarrier_ # tmp_op # "_" # suffix :
Intrinsic<[llvm_i1_ty], args,
[IntrConvergent, NoCapture<ArgIndex<0>>, IntrNoCallback]>;
def int_nvvm_mbarrier_ # tmp_op # "_relaxed_" # suffix :
Intrinsic<[llvm_i1_ty], args,
[IntrConvergent, NoCapture<ArgIndex<0>>, IntrNoCallback,
IntrArgMemOnly, IntrReadMem]>;
} // tl
} // parity
} // scope
} // op
// Generated within nvvm. Use for ldu on sm_20 or later. Second arg is the
// pointer's alignment.
let IntrProperties = [IntrReadMem, IntrArgMemOnly, IntrNoCallback, IntrWillReturn, NoCapture<ArgIndex<0>>] in {
def int_nvvm_ldu_global_i : Intrinsic<[llvm_anyint_ty], [llvm_anyptr_ty, llvm_i32_ty]>;
def int_nvvm_ldu_global_f : Intrinsic<[llvm_anyfloat_ty], [llvm_anyptr_ty, llvm_i32_ty]>;
def int_nvvm_ldu_global_p : Intrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty, llvm_i32_ty]>;
}
// Represents an explicit hole in the LLVM IR type system. It may be inserted by
// the compiler in cases where a pointer is of the wrong type. In the backend
// this intrinsic will be folded away and not equate to any instruction. It
// should not be used by any frontend and should only be considered well defined
// when added in the following cases:
//
// - NVPTXLowerArgs: When wrapping a byval pointer argument to a kernel
// function to convert the address space from generic (0) to param (101).
// This accounts for the fact that the parameter symbols will occupy this
// space when lowered during ISel.
//
def int_nvvm_internal_addrspace_wrap :
NVVMPureIntrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty],
[NoUndef<ArgIndex<0>>, NoUndef<RetIndex>]>;
// Move intrinsics, used in nvvm internally
let IntrProperties = [IntrNoMem] in {
def int_nvvm_move_i16 : DefaultAttrsIntrinsic<[llvm_i16_ty], [llvm_i16_ty]>;
def int_nvvm_move_i32 : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i32_ty]>;
def int_nvvm_move_i64 : DefaultAttrsIntrinsic<[llvm_i64_ty], [llvm_i64_ty]>;
def int_nvvm_move_float : DefaultAttrsIntrinsic<[llvm_float_ty], [llvm_float_ty]>;
def int_nvvm_move_double : DefaultAttrsIntrinsic<[llvm_double_ty], [llvm_double_ty]>;
def int_nvvm_move_ptr : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_anyptr_ty]>;
}
// For getting the handle from a texture or surface variable
def int_nvvm_texsurf_handle
: NVVMPureIntrinsic<[llvm_i64_ty], [llvm_metadata_ty, llvm_anyptr_ty]>;
def int_nvvm_texsurf_handle_internal
: NVVMPureIntrinsic<[llvm_i64_ty], [llvm_anyptr_ty]>;
/// Error / Warn
def int_nvvm_compiler_error : Intrinsic<[], [llvm_anyptr_ty]>;
def int_nvvm_compiler_warn : Intrinsic<[], [llvm_anyptr_ty]>;
def int_nvvm_reflect : NVVMBuiltin, NVVMPureIntrinsic<[llvm_i32_ty], [llvm_ptr_ty]>;
// isspacep.{const, global, local, shared}
foreach space = ["const", "global", "local", "shared", "shared_cluster"] in
def int_nvvm_isspacep_ # space : NVVMBuiltin,
NVVMPureIntrinsic<[llvm_i1_ty], [llvm_ptr_ty], [NoCapture<ArgIndex<0>>]>;
//
// Texture Fetch
//
let IntrProperties = [IntrReadMem, IntrNoCallback, IntrNoFree, IntrWillReturn] in {
foreach is_unified = [true, false] in {
defvar mode = !if(is_unified, "_unified", "");
defvar addr_args = !if(is_unified, [llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty]);
foreach vec = [V4F32, V4S32, V4U32] in {
foreach is_array = [true, false] in {
defvar array = !if(is_array, "_array", "");
defvar array_args = !if(is_array, [llvm_i32_ty], []<LLVMType>);
defvar base_args = !listconcat(addr_args, array_args);
def int_nvvm_tex # mode # _1d # array # _ # vec.Name # _s32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_i32_ty, 1)>;
def int_nvvm_tex # mode # _1d # array # _ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 1)>;
def int_nvvm_tex # mode # _1d # array # _level_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 2)>;
def int_nvvm_tex # mode # _1d # array # _grad_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 3)>;
def int_nvvm_tex # mode # _2d # array # _ # vec.Name # _s32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_i32_ty, 2)>;
def int_nvvm_tex # mode # _2d # array # _ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 2)>;
def int_nvvm_tex # mode # _2d # array # _level_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 3)>;
def int_nvvm_tex # mode # _2d # array # _grad_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 6)>;
if !not(is_array) then {
def int_nvvm_tex # mode # _3d_ # vec.Name # _s32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_i32_ty, 3)>;
def int_nvvm_tex # mode # _3d_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 3)>;
def int_nvvm_tex # mode # _3d_level_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 4)>;
def int_nvvm_tex # mode # _3d_grad_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 9)>;
}
def int_nvvm_tex # mode # _cube # array # _ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 3)>;
def int_nvvm_tex # mode # _cube # array # _level_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 4)>;
if is_unified then
def int_nvvm_tex # mode # _cube # array # _grad_ # vec.Name # _f32
: Intrinsic<vec.Types, base_args # !listsplat(llvm_float_ty, 9)>;
} // is_array
foreach comp = ["r", "g", "b", "a"] in {
def int_nvvm_tld4 # mode # _ # comp # _2d_ # vec.Name # _f32
: Intrinsic<vec.Types, addr_args # !listsplat(llvm_float_ty, 2)>;
} // comp
} // vec
} // is_unified
} // IntrProperties = [IntrReadMem]
//=== Surface Load
foreach clamp = ["clamp", "trap", "zero"] in {
foreach vec = [TV_I8, TV_I16, TV_I32, TV_I64,
TV_V2I8, TV_V2I16, TV_V2I32, TV_V2I64,
TV_V4I8, TV_V4I16, TV_V4I32] in {
let IntrProperties = [IntrNoCallback, IntrNoFree, IntrReadMem]
# !if(!ne(clamp, "trap"), [IntrWillReturn], []<IntrinsicProperty>) in {
def int_nvvm_suld_1d_ # vec.Name # _ # clamp
: Intrinsic<vec.Types,
[llvm_i64_ty, llvm_i32_ty]>;
def int_nvvm_suld_1d_array_ # vec.Name # _ # clamp
: Intrinsic<vec.Types,
[llvm_i64_ty, llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_suld_2d_ # vec.Name # _ # clamp
: Intrinsic<vec.Types,
[llvm_i64_ty, llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_suld_2d_array_ # vec.Name # _ # clamp
: Intrinsic<vec.Types,
[llvm_i64_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
def int_nvvm_suld_3d_ # vec.Name # _ # clamp
: Intrinsic<vec.Types,
[llvm_i64_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty]>;
}
} // vec
} // clamp
//===- Texture Query ------------------------------------------------------===//
foreach query = ["channel_order", "channel_data_type", "width", "height",
"depth", "array_size", "num_samples", "num_mipmap_levels"] in
def int_nvvm_txq_ # query : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i64_ty], [IntrNoMem]>;
//===- Surface Query ------------------------------------------------------===//
foreach query = ["channel_order", "channel_data_type", "width", "height",
"depth", "array_size"] in
def int_nvvm_suq_ # query : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_i64_ty], [IntrNoMem]>;
//===- Handle Query -------------------------------------------------------===//
foreach type = ["sampler", "surface", "texture"] in
def int_nvvm_istypep_ # type : NVVMBuiltin,
DefaultAttrsIntrinsic<[llvm_i1_ty], [llvm_i64_ty], [IntrNoMem]>;
//===- Surface Stores -----------------------------------------------------===//
multiclass SurfaceStoreIntrinsics<string clamp, TexVector vec> {
let IntrProperties = [IntrNoCallback, IntrNoFree, IntrWriteMem] #
!if(!ne(clamp, "trap"), [IntrWillReturn], []<IntrinsicProperty>) in {
def _1d_ # vec.Name # _ # clamp : NVVMBuiltin,
Intrinsic<[], [llvm_i64_ty, llvm_i32_ty] # vec.Types>;
def _1d_array_ # vec.Name # _ # clamp : NVVMBuiltin,
Intrinsic<[], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty] # vec.Types>;
def _2d_ # vec.Name # _ # clamp : NVVMBuiltin,
Intrinsic<[], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty] # vec.Types>;
def _2d_array_ # vec.Name # _ # clamp : NVVMBuiltin,
Intrinsic<[], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty] # vec.Types>;
def _3d_ # vec.Name # _ # clamp : NVVMBuiltin,
Intrinsic<[], [llvm_i64_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty] # vec.Types>;
}
}
// Unformatted
foreach clamp = ["clamp", "trap", "zero"] in
foreach vec = [TV_I8, TV_I16, TV_I32, TV_I64,
TV_V2I8, TV_V2I16, TV_V2I32, TV_V2I64,
TV_V4I8, TV_V4I16, TV_V4I32] in
defm int_nvvm_sust_b : SurfaceStoreIntrinsics<clamp, vec>;
// Formatted
foreach vec = [TV_I8, TV_I16, TV_I32,
TV_V2I8, TV_V2I16, TV_V2I32,
TV_V4I8, TV_V4I16, TV_V4I32] in
defm int_nvvm_sust_p : SurfaceStoreIntrinsics<"trap", vec>;
//
// Accessing special registers.
//
class PTXReadSRegIntrinsicNB_r32<list<IntrinsicProperty> properties = [], string name = "">
: NVVMPureIntrinsic<[llvm_i32_ty], [], [NoUndef<RetIndex>] # properties,
!if(!eq(name, ""), "", !strconcat("llvm.nvvm.read.ptx.sreg.", name))>;
class PTXReadSRegIntrinsic_r32<list<IntrinsicProperty> properties = []>
: PTXReadSRegIntrinsicNB_r32<properties>, NVVMBuiltin;
multiclass PTXReadSRegIntrinsic_v4i32<list<list<IntrinsicProperty>> properties = [[], [], [], []]> {
assert !eq(!size(properties), 4), "properties must be a list of 4 lists";
defvar suffixes = ["_x", "_y", "_z", "_w"];
foreach i = !range(suffixes) in
def suffixes[i] : PTXReadSRegIntrinsic_r32<properties[i]>;
}
// Same, but without automatic clang builtins. It will be used for
// registers that require particular GPU or PTX version.
multiclass PTXReadSRegIntrinsicNB_v4i32<list<list<IntrinsicProperty>> properties = [[], [], [], []]> {
assert !eq(!size(properties), 4), "properties must be a list of 4 lists";
defvar suffixes = ["_x", "_y", "_z", "_w"];
foreach i = !range(suffixes) in
def suffixes[i] : PTXReadSRegIntrinsicNB_r32<properties[i]>;
}
// Intrinsics to read registers with non-constant values. E.g. the values that
// do change over the kernel lifetime. Such reads should not be CSE'd.
class PTXReadNCSRegIntrinsic<LLVMType ty>
: Intrinsic<[ty], [], [IntrInaccessibleMemOnly, IntrNoCallback,
IntrNoFree, IntrWillReturn, NoUndef<RetIndex>]>,
NVVMBuiltin;
defvar MAX_BLOCK_ID_RANGE = [[Range<RetIndex, 0, MAX_BLOCK_SIZE_X>],
[Range<RetIndex, 0, MAX_BLOCK_SIZE_Y>],
[Range<RetIndex, 0, MAX_BLOCK_SIZE_Z>],
[Range<RetIndex, 0, 1>]];
defvar MAX_BLOCK_NID_RANGE = [[Range<RetIndex, 1, !add(MAX_BLOCK_SIZE_X, 1)>],
[Range<RetIndex, 1, !add(MAX_BLOCK_SIZE_Y, 1)>],
[Range<RetIndex, 1, !add(MAX_BLOCK_SIZE_Z, 1)>],
[Range<RetIndex, 0, 1>]];
defvar MAX_GRID_ID_RANGE = [[Range<RetIndex, 0, MAX_GRID_SIZE_X>],
[Range<RetIndex, 0, MAX_GRID_SIZE_Y>],
[Range<RetIndex, 0, MAX_GRID_SIZE_Z>],
[Range<RetIndex, 0, 1>]];
defvar MAX_GRID_NID_RANGE = [[Range<RetIndex, 1, !add(MAX_GRID_SIZE_X, 1)>],
[Range<RetIndex, 1, !add(MAX_GRID_SIZE_Y, 1)>],
[Range<RetIndex, 1, !add(MAX_GRID_SIZE_Z, 1)>],
[Range<RetIndex, 0, 1>]];
defm int_nvvm_read_ptx_sreg_tid : PTXReadSRegIntrinsic_v4i32<MAX_BLOCK_ID_RANGE>;
defm int_nvvm_read_ptx_sreg_ntid : PTXReadSRegIntrinsic_v4i32<MAX_BLOCK_NID_RANGE>;
def int_nvvm_read_ptx_sreg_laneid
: PTXReadSRegIntrinsic_r32<[Range<RetIndex, 0, WARP_SIZE>]>;
def int_nvvm_read_ptx_sreg_warpid : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_nwarpid : PTXReadSRegIntrinsic_r32;
defm int_nvvm_read_ptx_sreg_ctaid : PTXReadSRegIntrinsic_v4i32<MAX_GRID_ID_RANGE>;
defm int_nvvm_read_ptx_sreg_nctaid : PTXReadSRegIntrinsic_v4i32<MAX_GRID_NID_RANGE>;
def int_nvvm_read_ptx_sreg_smid : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_nsmid : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_gridid : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_lanemask_eq : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_lanemask_le : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_lanemask_lt : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_lanemask_ge : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_lanemask_gt : PTXReadSRegIntrinsic_r32;
def int_nvvm_read_ptx_sreg_clock : PTXReadNCSRegIntrinsic<llvm_i32_ty>;
def int_nvvm_read_ptx_sreg_clock64 : PTXReadNCSRegIntrinsic<llvm_i64_ty>;
def int_nvvm_read_ptx_sreg_globaltimer : PTXReadNCSRegIntrinsic<llvm_i64_ty>;
def int_nvvm_read_ptx_sreg_globaltimer_lo : PTXReadNCSRegIntrinsic<llvm_i32_ty>;
foreach i = 0...4 in
def int_nvvm_read_ptx_sreg_pm # i : PTXReadNCSRegIntrinsic<llvm_i32_ty>;
def int_nvvm_read_ptx_sreg_warpsize
: PTXReadSRegIntrinsic_r32<[Range<RetIndex, WARP_SIZE, !add(WARP_SIZE, 1)>]>;
foreach i = 0...31 in
def int_nvvm_read_ptx_sreg_envreg # i : PTXReadSRegIntrinsic_r32;
// sm90+, PTX7.8+
// Note: Since clusters are subdivisions of the grid, we conservatively use the
// maximum grid size as an upper bound for the clusterid and cluster_ctaid. In
// practice, the clusterid will likely be much smaller. The CUDA programming
// guide recommends 8 as a maximum portable value and H100s support 16.
defm int_nvvm_read_ptx_sreg_clusterid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_ID_RANGE>;
defm int_nvvm_read_ptx_sreg_nclusterid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_NID_RANGE>;
defm int_nvvm_read_ptx_sreg_cluster_ctaid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_ID_RANGE>;
defm int_nvvm_read_ptx_sreg_cluster_nctaid : PTXReadSRegIntrinsicNB_v4i32<MAX_GRID_NID_RANGE>;
def int_nvvm_read_ptx_sreg_cluster_ctarank : PTXReadSRegIntrinsicNB_r32;
def int_nvvm_read_ptx_sreg_cluster_nctarank : PTXReadSRegIntrinsicNB_r32;
//
// Reserved Shared Memory Intrinsics
//
def int_nvvm_read_ptx_sreg_reserved_smem_offset_begin
: PTXReadSRegIntrinsicNB_r32<name = "reserved_smem_offset_begin">;
def int_nvvm_read_ptx_sreg_reserved_smem_offset_end
: PTXReadSRegIntrinsicNB_r32<name = "reserved_smem_offset_end">;
def int_nvvm_read_ptx_sreg_reserved_smem_offset_cap
: PTXReadSRegIntrinsicNB_r32<name = "reserved_smem_offset_cap">;
def int_nvvm_read_ptx_sreg_reserved_smem_offset_0
: PTXReadSRegIntrinsicNB_r32<name = "reserved_smem_offset_0">;
def int_nvvm_read_ptx_sreg_reserved_smem_offset_1
: PTXReadSRegIntrinsicNB_r32<name = "reserved_smem_offset_1">;
def int_nvvm_read_ptx_sreg_total_smem_size :
PTXReadSRegIntrinsicNB_r32<name = "total_smem_size">;
def int_nvvm_read_ptx_sreg_aggr_smem_size :
PTXReadSRegIntrinsicNB_r32<name = "aggr_smem_size">;
def int_nvvm_read_ptx_sreg_dynamic_smem_size :
PTXReadSRegIntrinsicNB_r32<name = "dynamic_smem_size">;
//
// SHUFFLE
//
// Non-sync SHFL variants are pure functions of their SSA operands and the active
// thread mask. Memory properties (IntrReadMem + IntrInaccessibleMemOnly + IntrWillReturn)
// model reading the implicit mask state to ensure correct ordering with other convergent
// operations, preventing unsafe reordering while allowing DCE of truly dead code.
//
// Sync shfl variants have synchronization side effects modeled as
// inaccessible memory accesses to prevent DCE and reordering.
foreach is_sync = [false, true] in {
defvar CommonIntrinsicProps = [IntrInaccessibleMemOnly, IntrConvergent, IntrNoCallback];
defvar IntrinsicProps = !if(is_sync, CommonIntrinsicProps,
!listconcat(CommonIntrinsicProps, [IntrReadMem, IntrWillReturn]));
let IntrProperties = IntrinsicProps in {
foreach mode = ["up", "down", "bfly", "idx"] in {
foreach type = ["i32", "f32"] in {
foreach return_pred = [false, true] in {
defvar i = SHFL_INFO<is_sync, mode, type, return_pred>;
if i.withGccBuiltin then
def i.Name : NVVMBuiltin, Intrinsic<i.RetTy, i.ArgsTy>;
else
def i.Name : Intrinsic<i.RetTy, i.ArgsTy>;
}
}
}
}
}
//
// VOTE
//
let IntrProperties = [IntrInaccessibleMemOnly, IntrConvergent, IntrNoCallback] in {
def int_nvvm_vote_all : NVVMBuiltin, Intrinsic<[llvm_i1_ty], [llvm_i1_ty]>;
def int_nvvm_vote_any : NVVMBuiltin, Intrinsic<[llvm_i1_ty], [llvm_i1_ty]>;
def int_nvvm_vote_uni : NVVMBuiltin, Intrinsic<[llvm_i1_ty], [llvm_i1_ty]>;
def int_nvvm_vote_ballot : NVVMBuiltin, Intrinsic<[llvm_i32_ty], [llvm_i1_ty]>;
}
//
// VOTE.SYNC
//
let IntrProperties = [IntrInaccessibleMemOnly, IntrConvergent, IntrNoCallback] in {
def int_nvvm_vote_all_sync : NVVMBuiltin, Intrinsic<[llvm_i1_ty], [llvm_i32_ty, llvm_i1_ty]>;
def int_nvvm_vote_any_sync : NVVMBuiltin, Intrinsic<[llvm_i1_ty], [llvm_i32_ty, llvm_i1_ty]>;
def int_nvvm_vote_uni_sync : NVVMBuiltin, Intrinsic<[llvm_i1_ty], [llvm_i32_ty, llvm_i1_ty]>;
def int_nvvm_vote_ballot_sync : NVVMBuiltin, Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i1_ty]>;
}
//
// ACTIVEMASK
//
def int_nvvm_activemask : NVVMBuiltin,
Intrinsic<[llvm_i32_ty], [],
[IntrInaccessibleMemOnly, IntrConvergent, IntrNoCallback, IntrHasSideEffects]>;
//
// MATCH.SYNC
//
let IntrProperties = [IntrInaccessibleMemOnly, IntrConvergent, IntrNoCallback] in {
// match.any.sync.b32 mask, value
def int_nvvm_match_any_sync_i32 : NVVMBuiltin,
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
// match.any.sync.b64 mask, value
def int_nvvm_match_any_sync_i64 : NVVMBuiltin,
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i64_ty]>;
// match.all instruction have two variants -- one returns a single value, another
// returns a pair {value, predicate}. We currently only implement the latter as
// that's the variant exposed by CUDA API.
// match.all.sync.b32p mask, value
def int_nvvm_match_all_sync_i32p :
Intrinsic<[llvm_i32_ty, llvm_i1_ty], [llvm_i32_ty, llvm_i32_ty]>;
// match.all.sync.b64p mask, value
def int_nvvm_match_all_sync_i64p :
Intrinsic<[llvm_i32_ty, llvm_i1_ty], [llvm_i32_ty, llvm_i64_ty]>;
}
//
// ELECT.SYNC
//
// elect.sync dst|pred, membermask
def int_nvvm_elect_sync :
DefaultAttrsIntrinsic<[llvm_i32_ty, llvm_i1_ty], [llvm_i32_ty],
[IntrInaccessibleMemOnly, IntrConvergent]>;
//
// REDUX.SYNC
//
// redux.sync.op.u32 dst, src, membermask;
let IntrProperties = [IntrConvergent, IntrInaccessibleMemOnly, IntrNoCallback] in {
foreach op = ["umin", "umax", "add", "min", "max", "and", "xor", "or"] in
def int_nvvm_redux_sync_ # op : NVVMBuiltin,
Intrinsic<[llvm_i32_ty], [llvm_i32_ty, llvm_i32_ty]>;
// redux.sync.op.{abs}.{NaN}.f32 dst, src, membermask;
foreach binOp = ["min", "max"] in
foreach abs = ["", "_abs"] in
foreach NaN = ["", "_NaN"] in
def int_nvvm_redux_sync_f # binOp # abs # NaN : NVVMBuiltin,
Intrinsic<[llvm_float_ty], [llvm_float_ty, llvm_i32_ty]>;
}
//
// WGMMA fence instructions
//
// wgmma.fence.sync.aligned;
def int_nvvm_wgmma_fence_sync_aligned : Intrinsic<[], [], [IntrConvergent]>;
// wgmma.commit_group.sync.aligned;
def int_nvvm_wgmma_commit_group_sync_aligned
: Intrinsic<[], [], [IntrConvergent], "llvm.nvvm.wgmma.commit_group.sync.aligned">;
// wgmma.wait_group.sync.aligned N;
def int_nvvm_wgmma_wait_group_sync_aligned
: Intrinsic<[], [llvm_i64_ty], [IntrConvergent, ImmArg<ArgIndex<0>>], "llvm.nvvm.wgmma.wait_group.sync.aligned">;
//
// WMMA instructions
//
// WMMA.LOAD
class NVVM_WMMA_LD<WMMA_REGS Frag, string Layout, int WithStride>
: Intrinsic<Frag.regs,
!if(WithStride, [llvm_anyptr_ty, llvm_i32_ty], [llvm_anyptr_ty]),
[IntrWillReturn, IntrReadMem, IntrArgMemOnly, IntrNoCallback, ReadOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>],
WMMA_NAME_LDST<"load", Frag, Layout, WithStride>.intr_name>;
// WMMA.STORE.D
class NVVM_WMMA_ST<WMMA_REGS Frag, string Layout, int WithStride>
: Intrinsic<[],
!listconcat(
[llvm_anyptr_ty],
Frag.regs,
!if(WithStride, [llvm_i32_ty], [])),
[IntrWriteMem, IntrArgMemOnly, IntrNoCallback, WriteOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>],
WMMA_NAME_LDST<"store", Frag, Layout, WithStride>.intr_name>;
// Create all load/store variants
foreach layout = ["row", "col"] in {
foreach stride = [0, 1] in {
foreach frag = NVVM_MMA_OPS.all_ld_ops in
if NVVM_WMMA_LDST_SUPPORTED<frag, layout>.ret then
def WMMA_NAME_LDST<"load", frag, layout, stride>.record_name
: NVVM_WMMA_LD<frag, layout, stride>;
foreach frag = NVVM_MMA_OPS.all_st_ops in
if NVVM_WMMA_LDST_SUPPORTED<frag, layout>.ret then
def WMMA_NAME_LDST<"store", frag, layout, stride>.record_name
: NVVM_WMMA_ST<frag, layout, stride>;
}
}
// WMMA.MMA
class NVVM_MMA<WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D>
: Intrinsic<D.regs,
!listconcat(A.regs, B.regs, C.regs),
[IntrNoMem, IntrNoCallback]>;
foreach layout_a = ["row", "col"] in {
foreach layout_b = ["row", "col"] in {
foreach satf = [0, 1] in {
foreach rnd = ["", "rn", "rz", "rm", "rp"] in {
foreach op = NVVM_MMA_OPS.all_wmma_ops in {
foreach b1op = NVVM_MMA_B1OPS<op>.ret in {
if NVVM_WMMA_SUPPORTED<op, layout_a, layout_b, satf, rnd>.ret then {
def WMMA_NAME<layout_a, layout_b, satf, rnd, b1op,
op[0], op[1], op[2], op[3]>.record_name
: NVVM_MMA<op[0], op[1], op[2], op[3]>;
}
} // b1op
} // op
} // rnd
} // satf
} // layout_b
} // layout_a
foreach layout_a = ["row", "col"] in {
foreach layout_b = ["row", "col"] in {
foreach satf = [0, 1] in {
foreach op = NVVM_MMA_OPS.all_mma_ops in {
foreach b1op = NVVM_MMA_B1OPS<op>.ret in {
foreach kind = ["", "kind::f8f6f4"] in {
if NVVM_MMA_SUPPORTED<op, layout_a, layout_b, kind, satf>.ret then {
def MMA_NAME<layout_a, layout_b, satf, b1op, kind, op[0], op[1], op[2], op[3]>.record_name
: NVVM_MMA<op[0], op[1], op[2], op[3]>;
}
} // kind
} // b1op
} // op
} // satf
} // layout_b
} // layout_a
class NVVM_MMA_BLOCK_SCALE<WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D>
: Intrinsic<D.regs,
!listconcat(A.regs, B.regs, C.regs,
[
llvm_i32_ty, // scale-a-data
llvm_i16_ty, llvm_i16_ty, // byte-id-a, thread-id-a
llvm_i32_ty, // scale-b-data,
llvm_i16_ty, llvm_i16_ty, // byte-id-b, thread-id-b
]),
[IntrNoMem, IntrNoCallback]>;
foreach kind = ["mxf4", "mxf4nvf4", "mxf8f6f4"] in {
foreach scale_vec_size = ["", ".scale_1x", ".scale_2x", ".scale_4x"] in {
foreach stype = ["ue8m0", "ue4m3"] in {
foreach op = NVVM_MMA_OPS.all_mma_block_scale_ops in {
if NVVM_MMA_BLOCK_SCALE_SUPPORTED<op, kind, stype, scale_vec_size>.ret then {
def MMA_BLOCK_SCALE_NAME<kind, stype, scale_vec_size,
op[0], op[1], op[2], op[3]>.record_name
: NVVM_MMA_BLOCK_SCALE<op[0], op[1], op[2], op[3]>;
}
} // op
} // stype
} // scale_vec_size
} // kind
// MMA.SP
class NVVM_MMA_SP<WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D>
: Intrinsic<D.regs,
!listconcat(A.regs, B.regs, C.regs, [llvm_i32_ty], [llvm_i32_ty])> {
int pos = !size(!listconcat(A.regs, B.regs, C.regs, [llvm_i32_ty]));
// The range [0;num_threads) is for the sparsity selector that indicates the threads
// which contribute metadata.
int num_threads = !if(!or(!and(!eq(A.geom, "m16n8k32"), !eq(A.ptx_elt_type, "bf16")),
!and(!eq(A.geom, "m16n8k32"), !eq(A.ptx_elt_type, "f16")),
!and(!eq(A.geom, "m16n8k16"), !eq(A.ptx_elt_type, "tf32")),
!and(!eq(A.geom, "m16n8k32"), !eq(A.ptx_elt_type, "u8")),
!and(!eq(A.geom, "m16n8k32"), !eq(A.ptx_elt_type, "s8")),
!and(!eq(A.geom, "m16n8k64"), !eq(A.ptx_elt_type, "u4")),
!and(!eq(A.geom, "m16n8k64"), !eq(A.ptx_elt_type, "s4"))),
2,
!if(!and(!eq(A.geom, "m16n8k64"),
!or(!eq(A.ptx_elt_type, "u8"),
!eq(A.ptx_elt_type, "s8"),
!eq(A.ptx_elt_type, "e4m3"),
!eq(A.ptx_elt_type, "e5m2"),
!eq(A.ptx_elt_type, "e3m2"),
!eq(A.ptx_elt_type, "e2m3"),
!eq(A.ptx_elt_type, "e2m1"))),
1,
!if(!and(!eq(A.geom, "m16n8k128"),
!or(!eq(A.ptx_elt_type, "s4"),
!eq(A.ptx_elt_type, "u4"))),
1, 4)));
let IntrProperties = [IntrNoMem, IntrNoCallback, ImmArg<ArgIndex<pos>>,
Range<ArgIndex<pos>, 0, num_threads>];
}
foreach metadata = ["sp", "sp::ordered_metadata"] in {
foreach kind = ["", "kind::f8f6f4"] in {
foreach satf = [0, 1] in {
foreach op = NVVM_MMA_OPS.all_mma_sp_ops in {
if NVVM_MMA_SP_SUPPORTED<op, metadata, kind, satf>.ret then {
def MMA_SP_NAME<metadata, kind, satf,
op[0], op[1], op[2], op[3]>.record_name
: NVVM_MMA_SP<op[0], op[1], op[2], op[3]>;
}
} // op
} // satf
} // kind
} // metadata
// MMA.SP BLOCK SCALE
class NVVM_MMA_SP_BLOCK_SCALE<WMMA_REGS A, WMMA_REGS B, WMMA_REGS C, WMMA_REGS D>
: Intrinsic<D.regs,
!listconcat(A.regs, B.regs, C.regs,
[
llvm_i32_ty, // metadata
llvm_i32_ty, // sparsity selector
llvm_i32_ty, // scale-a-data
llvm_i16_ty, llvm_i16_ty, // byte-id-a, thread-id-a
llvm_i32_ty, // scale-b-data
llvm_i16_ty, llvm_i16_ty, // byte-id-b, thread-id-b
])> {
int pos = !size(!listconcat(A.regs, B.regs, C.regs, [llvm_i32_ty]));
// The range [0;num_threads) is for the sparsity selector that indicates the threads
// which contribute metadata.
// According to PTX ISA 9.0, the sparsity selector is always 0
// for sparse MMA block scale instructions
int num_threads = 1;
let IntrProperties = [IntrNoMem, IntrNoCallback, ImmArg<ArgIndex<pos>>,
Range<ArgIndex<pos>, 0, num_threads>];
}
// According to PTX ISA 9.0
// a_layout = ["row"], b_layout = ["col"], spvariant = ["sp::ordered_metadata"]
foreach kind = ["mxf4", "mxf4nvf4", "mxf8f6f4"] in {
foreach scale_vec_size = ["", ".scale_1x", ".scale_2x", ".scale_4x"] in {
foreach stype = ["ue8m0", "ue4m3"] in {
foreach op = NVVM_MMA_OPS.all_mma_sp_block_scale_ops in {
if NVVM_MMA_SP_BLOCK_SCALE_SUPPORTED<op, kind, stype, scale_vec_size>.ret then {
def MMA_SP_BLOCK_SCALE_NAME<kind, stype, scale_vec_size,
op[0], op[1], op[2], op[3]>.record_name
: NVVM_MMA_SP_BLOCK_SCALE<op[0], op[1], op[2], op[3]>;
}
} // op
} // stype
} // scale_vec_size
} // kind
// LDMATRIX
class NVVM_LDMATRIX<WMMA_REGS Frag, int Transposed>
: Intrinsic<Frag.regs, [llvm_anyptr_ty],
[IntrReadMem, IntrArgMemOnly, IntrNoCallback, IntrConvergent,
ReadOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>],
LDMATRIX_NAME<Frag, Transposed>.intr_name>;
foreach transposed = [0, 1] in {
foreach frag = NVVM_MMA_OPS.all_ldmatrix_ops in {
if NVVM_LDMATRIX_SUPPORTED<frag, transposed>.ret then {
def LDMATRIX_NAME<frag, transposed>.record_name
: NVVM_LDMATRIX<frag, transposed>;
}
}
}
// STMATRIX
class NVVM_STMATRIX<WMMA_REGS Frag, int Transposed>
: Intrinsic<[],
!listconcat([llvm_anyptr_ty], Frag.regs),
[IntrWriteMem, IntrArgMemOnly, IntrNoCallback, IntrConvergent,
WriteOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>],
STMATRIX_NAME<Frag, Transposed>.intr_name>;
foreach transposed = [0, 1] in {
foreach frag = NVVM_MMA_OPS.all_stmatrix_ops in {
if NVVM_STMATRIX_SUPPORTED<frag, transposed>.ret then {
def STMATRIX_NAME<frag, transposed>.record_name
: NVVM_STMATRIX<frag, transposed>;
}
}
}
// movmatrix
def int_nvvm_movmatrix_sync_aligned_m8n8_trans_b16
: Intrinsic<[llvm_i32_ty], [llvm_i32_ty],
[IntrInaccessibleMemOnly, IntrConvergent,
IntrNoCallback, IntrNoFree]>;
// MAPA
let IntrProperties = [IntrNoMem, IntrSpeculatable, NoCapture<ArgIndex<0>>] in {
def int_nvvm_mapa
: DefaultAttrsIntrinsic<[llvm_ptr_ty], [llvm_ptr_ty, llvm_i32_ty]>;
def int_nvvm_mapa_shared_cluster
: DefaultAttrsIntrinsic<[llvm_shared_cluster_ptr_ty], [llvm_shared_ptr_ty, llvm_i32_ty]>;
}
// GETCTARANK
let IntrProperties = [IntrNoMem, IntrSpeculatable, NoCapture<ArgIndex<0>>] in {
def int_nvvm_getctarank
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_ptr_ty]>;
def int_nvvm_getctarank_shared_cluster
: DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_shared_ptr_ty]>;
}
def int_nvvm_is_explicit_cluster
: NVVMPureIntrinsic<[llvm_i1_ty], [], [NoUndef<RetIndex>],
"llvm.nvvm.is_explicit_cluster">;
// Setmaxnreg inc/dec intrinsics
// The imm-arg should be in the range: 24 <= val <= 256
foreach op = ["dec", "inc"] in
def int_nvvm_setmaxnreg_ # op # _sync_aligned_u32
: DefaultAttrsIntrinsic<[], [llvm_i32_ty],
[IntrConvergent, IntrNoMem, IntrHasSideEffects,
ImmArg<ArgIndex<0>>, Range<ArgIndex<0>, 24, 257>]>;
// Exit
def int_nvvm_exit : NVVMBuiltin,
Intrinsic<[], [], [IntrConvergent, IntrInaccessibleMemOnly, IntrNoReturn]>;
class DefaultAttrsIntrinsicFlags<list<LLVMType> ret_types,
list<LLVMType> param_types,
list<LLVMType> flags,
list<IntrinsicProperty> intr_properties,
string name = "">
: DefaultAttrsIntrinsic<
ret_types,
!listconcat(param_types, flags),
!listconcat(intr_properties,
!foreach(i, !range(flags),
ImmArg<ArgIndex<!add(i, !size(param_types))>>)),
name>;
// TMA Tensor Copy Intrinsics: S2G -> From Shared to Global memory variants
foreach dim = 1...5 in {
defvar tensor_dim_args = !listsplat(llvm_i32_ty, dim);
foreach mode = !if(!ge(dim, 3), ["tile", "im2col"], ["tile"]) in {
def int_nvvm_cp_async_bulk_tensor_s2g_ # mode # _ # dim # d :
DefaultAttrsIntrinsicFlags<[],
!listconcat([llvm_shared_ptr_ty, // src_smem_ptr
llvm_ptr_ty], // tensormap_ptr
tensor_dim_args, // actual tensor dims
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, ReadOnly<ArgIndex<0>>, ReadOnly<ArgIndex<1>>]>;
// Intrinsics for TMA Copy with reduction
foreach red_op = ["add", "min", "max", "inc", "dec", "and", "or", "xor"] in
def int_nvvm_cp_async_bulk_tensor_reduce_ # red_op # _ # mode # _ # dim # d :
DefaultAttrsIntrinsicFlags<[],
!listconcat([llvm_shared_ptr_ty, // src_smem_ptr
llvm_ptr_ty], // tensormap_ptr
tensor_dim_args, // actual tensor dims
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, ReadOnly<ArgIndex<0>>, ReadOnly<ArgIndex<1>>]>;
}
}
// TMA S2G tile::scatter4
def int_nvvm_cp_async_bulk_tensor_s2g_tile_scatter4_2d
: DefaultAttrsIntrinsicFlags<[],
!listconcat([llvm_shared_ptr_ty, // src_smem_ptr
llvm_ptr_ty], // tensormap_ptr
!listsplat(llvm_i32_ty, 5), // dims
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, ReadOnly<ArgIndex<0>>, ReadOnly<ArgIndex<1>>]>;
// TMA Tensor Copy Intrinsics: G2S -> From Global to Shared memory variants
foreach dim = 1...5 in {
defvar tensor_dim_args = !listsplat(llvm_i32_ty, dim);
foreach mode = !if(!ge(dim, 3), ["tile", "im2col", "im2col_w", "im2col_w_128"], ["tile"]) in {
defvar is_im2col = !eq(mode, "im2col");
defvar is_im2colw = !or(!eq(mode, "im2col_w"), !eq(mode, "im2col_w_128"));
// For im2col_w/w128 modes, the num_offsets is always 2.
// For im2col mode, the num_offsets is (dim - 2).
defvar num_im2col_offsets = !if(is_im2colw, 2, !if(is_im2col, !add(dim, -2), 0));
defvar im2col_offsets_args = !listsplat(llvm_i16_ty, num_im2col_offsets);
defvar g2s_params = !listconcat(
[llvm_shared_cluster_ptr_ty, // dst_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_ptr_ty], // tensormap_ptr
tensor_dim_args, // actual tensor dims
im2col_offsets_args, // im2col offsets
[llvm_i16_ty, // cta_mask
llvm_i64_ty]); // cache_hint
defvar g2s_flags = [llvm_i1_ty, // Flag for cta_mask
llvm_i1_ty, // Flag for cache_hint
llvm_i32_ty]; // Flag for cta_group
defvar cta_group_idx = !add(
!size(g2s_params),
!sub(!size(g2s_flags), 1));
defvar g2s_props = [IntrConvergent,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>,
// Allowed values for cta_group are {0,1,2} i.e [0, 3).
Range<ArgIndex<cta_group_idx>, 0, 3>];
def int_nvvm_cp_async_bulk_tensor_g2s_ # mode # _ # dim # d :
DefaultAttrsIntrinsicFlags<[], g2s_params, g2s_flags, g2s_props>;
def int_nvvm_cp_async_bulk_tensor_prefetch_ # mode # _ # dim # d :
DefaultAttrsIntrinsicFlags<[],
!listconcat([llvm_ptr_ty], // tensormap_ptr
tensor_dim_args, // actual tensor dims
im2col_offsets_args, // im2col offsets
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, ReadOnly<ArgIndex<0>>]>;
def int_nvvm_cp_async_bulk_tensor_g2s_cta_ # mode # _ # dim # d :
DefaultAttrsIntrinsicFlags<[],
!listconcat([llvm_shared_ptr_ty, // dst_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_ptr_ty], // tensormap_ptr
tensor_dim_args, // actual tensor dims
im2col_offsets_args, // im2col offsets
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>]>;
}
}
// TMA copy for tile::gather4
def int_nvvm_cp_async_bulk_tensor_g2s_tile_gather4_2d
: DefaultAttrsIntrinsicFlags<[],
!listconcat(
[llvm_shared_cluster_ptr_ty, // dst_shared_cluster_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_ptr_ty], // tensormap_ptr
!listsplat(llvm_i32_ty, 5), // co-ordinates
[llvm_i16_ty, // cta_mask
llvm_i64_ty]), // cache_hint
[llvm_i1_ty, // Flag for cta_mask
llvm_i1_ty, // Flag for cache_hint
llvm_i32_ty], // Flag for cta_group
[IntrConvergent,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>,
// Allowed values for cta_group are {0,1,2} i.e [0, 3).
Range<ArgIndex<12>, 0, 3>]>;
def int_nvvm_cp_async_bulk_tensor_g2s_cta_tile_gather4_2d
: DefaultAttrsIntrinsicFlags<[],
!listconcat(
[llvm_shared_ptr_ty, // dst_shared_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_ptr_ty], // tensormap_ptr
!listsplat(llvm_i32_ty, 5), // co-ordinates
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>]>;
// TMA prefetch for tile::gather4
def int_nvvm_cp_async_bulk_tensor_prefetch_tile_gather4_2d
: DefaultAttrsIntrinsicFlags<[],
!listconcat([llvm_ptr_ty], // tensormap_ptr
!listsplat(llvm_i32_ty, 5), // co-ordinates
[llvm_i64_ty]), // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, ReadOnly<ArgIndex<0>>]>;
// Intrinsics for Prefetch and Prefetchu
let IntrProperties = [IntrArgMemOnly, ReadOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>] in {
foreach level = ["L1", "L2"] in {
def int_nvvm_prefetch_ # level : DefaultAttrsIntrinsic<[], [llvm_ptr_ty]>;
def int_nvvm_prefetch_global_ # level : DefaultAttrsIntrinsic<[], [llvm_global_ptr_ty]>;
def int_nvvm_prefetch_local_ # level : DefaultAttrsIntrinsic<[], [llvm_local_ptr_ty]>;
}
def int_nvvm_prefetch_tensormap : DefaultAttrsIntrinsic<[], [llvm_anyptr_ty]>;
foreach eviction_priority = ["evict_normal", "evict_last"] in
def int_nvvm_prefetch_global_L2_ # eviction_priority : DefaultAttrsIntrinsic<[], [llvm_global_ptr_ty]>;
def int_nvvm_prefetchu_L1 : DefaultAttrsIntrinsic<[], [llvm_ptr_ty]>;
}
// applypriority
let IntrProperties = [IntrArgMemOnly, ReadOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>,
ImmArg<ArgIndex<1>>] in {
def int_nvvm_applypriority_global_L2_evict_normal
: DefaultAttrsIntrinsic<[], [llvm_global_ptr_ty, llvm_i64_ty]>;
def int_nvvm_applypriority_L2_evict_normal
: DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_i64_ty]>;
}
// discard
let IntrProperties = [NoCapture<ArgIndex<0>>, ImmArg<ArgIndex<1>>, IntrHasSideEffects] in {
def int_nvvm_discard_global_L2 : DefaultAttrsIntrinsic<[], [llvm_global_ptr_ty, llvm_i64_ty]>;
def int_nvvm_discard_L2 : DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_i64_ty]>;
}
// Intrinsics for Bulk Copy using TMA (non-tensor)
// From Global to Shared Cluster
def int_nvvm_cp_async_bulk_global_to_shared_cluster
: DefaultAttrsIntrinsicFlags<[],
[llvm_shared_cluster_ptr_ty, // dst_shared_cluster_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_global_ptr_ty, // src_gmem_ptr
llvm_i32_ty, // copy_size
llvm_i16_ty, // cta_mask
llvm_i64_ty], // cache_hint
[llvm_i1_ty, // Flag for cta_mask
llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, IntrArgMemOnly,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>]>;
// From Global to Shared CTA
def int_nvvm_cp_async_bulk_global_to_shared_cta
: DefaultAttrsIntrinsicFlags<[],
[llvm_shared_ptr_ty, // dst_shared_cta_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_global_ptr_ty, // src_gmem_ptr
llvm_i32_ty, // copy_size
llvm_i64_ty], // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, IntrArgMemOnly,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>]>;
// From Shared CTA to Shared Cluster
def int_nvvm_cp_async_bulk_shared_cta_to_cluster
: DefaultAttrsIntrinsic<[],
[llvm_shared_cluster_ptr_ty, // dst_shared_cluster_ptr
llvm_shared_ptr_ty, // mbarrier_ptr
llvm_shared_ptr_ty, // src_smem_ptr
llvm_i32_ty], // copy_size
[IntrConvergent, IntrArgMemOnly,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<2>>]>;
// From Shared CTA to Global memory
def int_nvvm_cp_async_bulk_shared_cta_to_global
: DefaultAttrsIntrinsicFlags<[],
[llvm_global_ptr_ty, // dst_gmem_ptr
llvm_shared_ptr_ty, // src_smem_ptr
llvm_i32_ty, // copy_size
llvm_i64_ty], // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, IntrArgMemOnly,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<1>>]>;
// From Shared CTA to Global memory with bytemask
def int_nvvm_cp_async_bulk_shared_cta_to_global_bytemask
: DefaultAttrsIntrinsic<[],
[llvm_global_ptr_ty, // dst_gmem_ptr
llvm_shared_ptr_ty, // src_smem_ptr
llvm_i32_ty, // copy_size
llvm_i64_ty, // cache_hint
llvm_i1_ty, // Flag for cache_hint
llvm_i16_ty], // byte_mask
[IntrConvergent, IntrArgMemOnly,
WriteOnly<ArgIndex<0>>, ReadOnly<ArgIndex<1>>,
ImmArg<ArgIndex<4>>]>;
// Intrinsics for Bulk Copy Prefetch L2
def int_nvvm_cp_async_bulk_prefetch_L2
: DefaultAttrsIntrinsicFlags<[],
[llvm_global_ptr_ty, // src_gmem_ptr
llvm_i32_ty, // copy_size
llvm_i64_ty], // cache_hint
[llvm_i1_ty], // Flag for cache_hint
[IntrConvergent, IntrArgMemOnly,
NoCapture<ArgIndex<0>>, ReadOnly<ArgIndex<0>>]>;
def int_nvvm_griddepcontrol_launch_dependents : Intrinsic<[], [], [IntrNoMem, IntrHasSideEffects]>;
def int_nvvm_griddepcontrol_wait : Intrinsic<[], [], [IntrNoMem, IntrHasSideEffects]>;
//
// Tcgen05 family of Intrinsics
//
// Tcgen05 alloc/dealloc related intrinsics
foreach cta_group = ["cg1", "cg2"] in {
def int_nvvm_tcgen05_alloc_ # cta_group : Intrinsic<[],
[llvm_ptr_ty, // dst_ptr
llvm_i32_ty] , // num_columns
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly,
WriteOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_alloc_shared_ # cta_group : Intrinsic<[],
[llvm_shared_ptr_ty, // dst_ptr
llvm_i32_ty], // num_columns
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly,
WriteOnly<ArgIndex<0>>, NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_dealloc_ # cta_group : Intrinsic<[],
[llvm_tmem_ptr_ty, // tmem_addr
llvm_i32_ty], // num_columns
[IntrConvergent, IntrArgMemOnly,
NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_relinq_alloc_permit_ # cta_group : Intrinsic<[], [],
[IntrConvergent, IntrInaccessibleMemOnly]>;
def int_nvvm_tcgen05_commit_ # cta_group : Intrinsic<[],
[llvm_ptr_ty], // mbar_ptr
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly,
NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_commit_shared_ # cta_group : Intrinsic<[],
[llvm_shared_ptr_ty], // mbar_ptr
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly,
NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_commit_mc_ # cta_group : Intrinsic<[],
[llvm_ptr_ty, llvm_i16_ty], // mbar_ptr, cta_mask
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly,
NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_commit_mc_shared_ # cta_group : Intrinsic<[],
[llvm_shared_ptr_ty, llvm_i16_ty], // mbar_ptr, cta_mask
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly,
NoCapture<ArgIndex<0>>]>;
def int_nvvm_tcgen05_shift_down_ # cta_group : Intrinsic<[],
[llvm_tmem_ptr_ty], // tmem_addr
[IntrConvergent, IntrArgMemOnly,
NoCapture<ArgIndex<0>>]>;
}
// Tcgen05 wait_ld/st intrinsics
def int_nvvm_tcgen05_wait_ld : Intrinsic<[], [],
[IntrConvergent, IntrInaccessibleMemOnly]>;
def int_nvvm_tcgen05_wait_st : Intrinsic<[], [],
[IntrConvergent, IntrInaccessibleMemOnly]>;
// Tcgen05 Fence intrinsics
def int_nvvm_tcgen05_fence_before_thread_sync : Intrinsic<[], [],
[IntrNoMem, IntrHasSideEffects]>;
def int_nvvm_tcgen05_fence_after_thread_sync : Intrinsic<[], [],
[IntrNoMem, IntrHasSideEffects]>;
// Tcgen05 cp intrinsics
foreach cta_group = ["cg1", "cg2"] in {
foreach src_fmt = ["", "b6x16_p32", "b4x16_p64"] in {
foreach shape = ["128x256b", "4x256b", "128x128b",
"64x128b_warpx2_02_13",
"64x128b_warpx2_01_23",
"32x128b_warpx4"] in {
defvar name = "llvm.nvvm.tcgen05.cp." #
StrJoin<".", [shape, src_fmt, cta_group]>.ret;
defvar intrinsic_name = IntrinsicName<name>;
def intrinsic_name.record_name : Intrinsic<[],
[llvm_tmem_ptr_ty, // tmem_addr
llvm_i64_ty], // smem descriptor
[IntrConvergent, IntrInaccessibleMemOrArgMemOnly, NoCapture<ArgIndex<0>>],
intrinsic_name.intr_name>;
}
}
}
// Tcgen05 ld intrinsics
class NVVM_TCGEN05_LD<string Shape, int Num> :
Intrinsic<[NVVM_TCGEN05_LDST_ACCESS_SIZE<Shape, Num>.type],
!listconcat([llvm_tmem_ptr_ty],
!if(!eq(Shape, "16x32bx2"), [llvm_i64_ty], []),
[llvm_i1_ty]),
!listconcat([IntrConvergent, IntrArgMemOnly, NoCapture<ArgIndex<0>>],
!if(!eq(Shape, "16x32bx2"),
[ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<2>>],
[ImmArg<ArgIndex<1>>]))>;
// Tcgen05 ld.red intrinsics
class NVVM_TCGEN05_LD_RED<string Shape, int Num, string RedValTy> :
DefaultAttrsIntrinsicFlags<
[NVVM_TCGEN05_LDST_ACCESS_SIZE<Shape, Num, RedValTy>.type, LLVMType<!cast<ValueType>(RedValTy)>],
[llvm_tmem_ptr_ty],
!listconcat(!if(!eq(Shape, "16x32bx2"), [llvm_i64_ty], []),
[llvm_i32_ty], // RedOp
!if(!eq(RedValTy, "f32"), [llvm_i1_ty, llvm_i1_ty], [])), // abs, nan
[IntrConvergent, IntrArgMemOnly, NoCapture<ArgIndex<0>>]>;
// Tcgen05 st intrinsics
class NVVM_TCGEN05_ST<string Shape, int Num> :
Intrinsic<[],
!listconcat([llvm_tmem_ptr_ty],
!if(!eq(Shape, "16x32bx2"), [llvm_i64_ty], []),
[NVVM_TCGEN05_LDST_ACCESS_SIZE<Shape, Num>.type],
[llvm_i1_ty]),
!listconcat([IntrConvergent, IntrArgMemOnly, NoCapture<ArgIndex<0>>],
!if(!eq(Shape, "16x32bx2"),
[ImmArg<ArgIndex<1>>, ImmArg<ArgIndex<3>>],
[ImmArg<ArgIndex<2>>]))>;
// tcgen05 ld/st intrinsics
foreach shape = ["16x64b", "16x128b", "16x256b", "32x32b", "16x32bx2"] in {
foreach num = 0...8 in {
if NVVM_TCGEN05_LDST_ACCESS_SIZE<shape, num>.valid then {
def int_nvvm_tcgen05_ld_ # shape # _x # !shl(1, num) :
NVVM_TCGEN05_LD<shape, num>;
def int_nvvm_tcgen05_st_ # shape # _x # !shl(1, num) :
NVVM_TCGEN05_ST<shape, num>;
}
}
}
// tcgen05.ld.red intrinsics
foreach shape = ["32x32b", "16x32bx2"] in {
// num starts with x2 (1 << 1) as tcgen05.ld.red does not support x1
foreach num = 1...8 in {
foreach ty = ["f32", "i32"] in {
if NVVM_TCGEN05_LDST_ACCESS_SIZE<shape, num>.valid then {
def IntrinsicName<"llvm.nvvm.tcgen05.ld.red." # shape
# ".x" # !shl(1, num) # "." # ty>.record_name:
NVVM_TCGEN05_LD_RED<shape, num, ty>;
} // valid
} // ty
} // num
} // shape
//
// Bulk store intrinsics
//
let IntrProperties = [IntrArgMemOnly, IntrWriteMem, WriteOnly<ArgIndex<0>>,
NoCapture<ArgIndex<0>>, ImmArg<ArgIndex<2>>] in {
def int_nvvm_st_bulk :
DefaultAttrsIntrinsic<[], [llvm_ptr_ty, llvm_i64_ty, llvm_i64_ty]>;
def int_nvvm_st_bulk_shared_cta :
DefaultAttrsIntrinsic<[], [llvm_shared_ptr_ty, llvm_i64_ty, llvm_i64_ty]>;
}
//
// clusterlaunchcontorl Intrinsics
//
// clusterlaunchcontrol.try_cancel
def int_nvvm_clusterlaunchcontrol_try_cancel_async_shared
: DefaultAttrsIntrinsic<[], [llvm_shared_ptr_ty, llvm_shared_ptr_ty],
[IntrHasSideEffects, IntrArgMemOnly],
"llvm.nvvm.clusterlaunchcontrol.try_cancel.async.shared">;
def int_nvvm_clusterlaunchcontrol_try_cancel_async_multicast_shared
: DefaultAttrsIntrinsic<[], [llvm_shared_ptr_ty, llvm_shared_ptr_ty],
[IntrHasSideEffects, IntrArgMemOnly],
"llvm.nvvm.clusterlaunchcontrol.try_cancel.async.multicast.shared">;
// clusterlaunchcontrol.query_cancel.is_canceled
def int_nvvm_clusterlaunchcontrol_query_cancel_is_canceled
: NVVMPureIntrinsic<[llvm_i1_ty], [llvm_i128_ty], [],
"llvm.nvvm.clusterlaunchcontrol.query_cancel.is_canceled">;
foreach dim = ["x", "y", "z"] in
def int_nvvm_clusterlaunchcontrol_query_cancel_get_first_ctaid_ # dim
: NVVMPureIntrinsic<[llvm_i32_ty], [llvm_i128_ty], [],
"llvm.nvvm.clusterlaunchcontrol.query_cancel.get_first_ctaid." # dim>;
//
// tcgen05.mma intrinsics
//
foreach sparse = [0, 1] in {
foreach space = ["tensor", "shared"] in {
foreach scale_d = [0, 1] in {
foreach ashift = !if(!eq(space, "tensor"), [0, 1], [0]) in {
defvar mma = NVVM_TCGEN05_MMA<sparse, space, ashift, scale_d>;
defvar args = !listconcat(
mma.common_args,
!if(!eq(scale_d, 1), [llvm_i64_ty], []) // scale_d_imm
);
defvar flags = [llvm_i32_ty, // kind
llvm_i32_ty, // cta_group
llvm_i32_ty]; // collector_usage_a
defvar nargs = !size(args);
defvar scale_d_imm = ArgIndex<!sub(nargs, 1)>;
defvar scale_d_imm_range = [ImmArg<scale_d_imm>, Range<scale_d_imm, 0, 16>];
// Check if this is the specific llvm.nvvm.tcgen05.mma.tensor intrinsic.
defvar is_target_intrinsic = !and(!eq(sparse, 0),
!eq(space, "tensor"),
!eq(scale_d, 0),
!eq(ashift, 0));
defvar base_properties = !listconcat(
mma.common_intr_props,
!if(!eq(scale_d, 1), scale_d_imm_range, []),
[Range<ArgIndex<nargs>, 0, !if(!eq(scale_d, 1), 2, 4)>, // kind
Range<ArgIndex<!add(nargs, 1)>, 1, 3>, // cta_group
Range<ArgIndex<!add(nargs, 2)>, 0,
!if(!eq(ashift, 1), 2, 4)> // collector_usage
]
);
defvar intrinsic_properties = !if(is_target_intrinsic,
!listconcat(base_properties,
[ArgInfo<ArgIndex<nargs>, [ArgName<"kind">, ImmArgPrinter<"printTcgen05MMAKind">]>,
ArgInfo<ArgIndex<!add(nargs, 1)>, [ArgName<"cta_group">]>,
ArgInfo<ArgIndex<!add(nargs, 2)>, [ArgName<"collector">, ImmArgPrinter<"printTcgen05CollectorUsageOp">]>]),
base_properties);
def mma.record_name:
DefaultAttrsIntrinsicFlags<[], args, flags, intrinsic_properties,
mma.intr_name>;
} // ashift
} // scale_d
} // space
} // sparse
//
// tcgen05.mma disable_output_lane intrinsics
//
foreach sparse = [0, 1] in {
foreach space = ["tensor", "shared"] in {
foreach cta_group = [1, 2] in {
foreach scale_d = [0, 1] in {
foreach ashift = !if(!eq(space, "tensor"), [0, 1], [0]) in {
defvar mma = NVVM_TCGEN05_MMA_DISABLE_OUTPUT_LANE<
sparse, space, cta_group, ashift, scale_d>;
defvar disable_output_lane_type =
!if(!eq(cta_group, 1), llvm_v4i32_ty, llvm_v8i32_ty);
defvar args = !listconcat(
mma.common_args,
!if(!eq(scale_d, 1), [llvm_i64_ty], []),
[disable_output_lane_type]
);
defvar flags = [llvm_i32_ty, // kind_flag
llvm_i32_ty]; // collector_usage_a_flag
defvar nargs = !size(args);
defvar scale_d_imm = ArgIndex<!sub(nargs, 2)>;
defvar scale_d_imm_range = [ImmArg<scale_d_imm>, Range<scale_d_imm, 0, 16>];
defvar intrinsic_properties = !listconcat(
mma.common_intr_props,
!if(!eq(scale_d, 1), scale_d_imm_range, []),
[Range<ArgIndex<nargs>, 0, !if(!eq(scale_d, 1), 2, 4)>,
Range<ArgIndex<!add(nargs, 1)>, 0, !if(!eq(ashift, 1), 2, 4)>]
);
def mma.record_name : DefaultAttrsIntrinsicFlags<[], args, flags,
intrinsic_properties, mma.intr_name>;
} // ashift
} // scale_d
} // cta_group
} // space
} // sparse
//
// tcgen05.mma block_scale intrinsics
//
foreach sparse = [0, 1] in {
foreach space = ["tensor", "shared"] in {
foreach kind = ["mxf8f6f4", "mxf4", "mxf4nvf4"] in {
foreach scale_vec_size = ["", ".block16", ".block32"] in {
defvar mma = NVVM_TCGEN05_MMA_BLOCKSCALE<sparse, space, kind, scale_vec_size>;
defvar args = !listconcat(mma.common_args,
[llvm_tmem_ptr_ty, // scale_a
llvm_tmem_ptr_ty]); // scale_b
defvar flags = [llvm_i32_ty, // cta_group
llvm_i32_ty]; // collector_usage_a
defvar nargs = !size(args);
defvar cta_group = ArgIndex<nargs>;
defvar collector_usage = ArgIndex<!add(nargs, 1)>;
if NVVM_TCGEN05_MMA_BLOCKSCALE_SUPPORTED<kind, scale_vec_size>.ret then {
def mma.record_name : DefaultAttrsIntrinsicFlags<[], args, flags,
!listconcat(mma.common_intr_props,
[Range<cta_group, 1, 3>,
Range<collector_usage, 0, 4>]),
mma.intr_name>;
}
} // scale_vec_size
} // kind
} // space
} // sparse
//
// tcgen05.mma ws intrinsics
//
foreach sparse = [0, 1] in {
foreach space = ["tensor", "shared"] in {
foreach zero_col_mask = [0, 1] in {
defvar mma = NVVM_TCGEN05_MMA_WS<sparse, space, zero_col_mask>;
defvar args = !listconcat(
mma.common_args,
!if(!eq(zero_col_mask, 1), [llvm_i64_ty], [])
);
defvar flags = [llvm_i32_ty, // kind
llvm_i32_ty, // collector_buffer_b
llvm_i32_ty]; // collector_usage_b_op
defvar nargs = !size(args);
defvar intrinsic_properties = !listconcat(
mma.common_intr_props,
[Range<ArgIndex<nargs>, 0, 4>,
Range<ArgIndex<!add(nargs, 1)>, 0, 4>,
Range<ArgIndex<!add(nargs, 2)>, 0, 4>]
);
def mma.record_name:
DefaultAttrsIntrinsicFlags<[], args, flags, intrinsic_properties,
mma.intr_name>;
} // zero_col_mask
} // space
} // sparse
//
// tensormap.replace intrinsics
//
let IntrProperties = [IntrArgMemOnly, IntrWriteMem, NoCapture<ArgIndex<0>>] in {
def int_nvvm_tensormap_replace_global_address :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i64_ty], []>;
def int_nvvm_tensormap_replace_rank :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty], []>;
}
let IntrProperties = [IntrArgMemOnly, ImmArg<ArgIndex<1>>, IntrWriteMem,
NoCapture<ArgIndex<0>>] in {
def int_nvvm_tensormap_replace_global_stride :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty, llvm_i64_ty], []>;
foreach tmap_field = ["box_dim", "global_dim", "element_stride"] in {
def int_nvvm_tensormap_replace_ # tmap_field :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty, llvm_i32_ty], []>;
}
}
def int_nvvm_tensormap_replace_elemtype :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty],
[IntrArgMemOnly, IntrWriteMem, ImmArg<ArgIndex<1>>, NoCapture<ArgIndex<0>>,
Range<ArgIndex<1>, 0, 16>,
ArgInfo<ArgIndex<1>, [ArgName<"elemtype">,
ImmArgPrinter<"printTensormapElemType">]>]>;
def int_nvvm_tensormap_replace_interleave_layout :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty],
[IntrArgMemOnly, IntrWriteMem, ImmArg<ArgIndex<1>>, NoCapture<ArgIndex<0>>,
Range<ArgIndex<1>, 0, 3>,
ArgInfo<ArgIndex<1>, [ArgName<"interleave_layout">,
ImmArgPrinter<"printTensormapInterleaveLayout">]>]>;
def int_nvvm_tensormap_replace_swizzle_mode :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty],
[IntrArgMemOnly, IntrWriteMem, ImmArg<ArgIndex<1>>, NoCapture<ArgIndex<0>>,
Range<ArgIndex<1>, 0, 5>,
ArgInfo<ArgIndex<1>, [ArgName<"swizzle_mode">,
ImmArgPrinter<"printTensormapSwizzleMode">]>]>;
def int_nvvm_tensormap_replace_swizzle_atomicity :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty],
[IntrArgMemOnly, IntrWriteMem, ImmArg<ArgIndex<1>>, NoCapture<ArgIndex<0>>,
Range<ArgIndex<1>, 0, 4>,
ArgInfo<ArgIndex<1>, [ArgName<"swizzle_atomicity">,
ImmArgPrinter<"printTensormapSwizzleAtomicity">]>]>;
def int_nvvm_tensormap_replace_fill_mode :
DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty],
[IntrArgMemOnly, IntrWriteMem, ImmArg<ArgIndex<1>>, NoCapture<ArgIndex<0>>,
Range<ArgIndex<1>, 0, 2>,
ArgInfo<ArgIndex<1>, [ArgName<"fill_mode">,
ImmArgPrinter<"printTensormapFillMode">]>]>;
} // let TargetPrefix = "nvvm"