//===- 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>. // 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 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: mnk. E.g. m8n32k16 // Frag: [a|b|c|d] ([x1|x2|x4] for ldmatrix) // PtxEltType: PTX type for the element. class WMMA_REGS { 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 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 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 { list 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 signature = MMA_SIGNATURE.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 signature = MMA_SIGNATURE.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 signature = MMA_SIGNATURE.ret; string record_name = "int_nvvm_mma_block_scale" # "_" # A.geom # "_row_col" # "_" # Kind # !subst(".", "_", ScaleVecSize) # signature # "_" # SType; } class MMA_SP_NAME { string signature = MMA_SIGNATURE.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 signature = MMA_SIGNATURE.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 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 { defvar name = "llvm.nvvm.ldmatrix.sync.aligned" # "." # Frag.geom # "." # Frag.frag # !if(Trans, ".trans", "") # "." # Frag.ptx_elt_type ; string intr_name = IntrinsicName.intr_name; string record_name = IntrinsicName.record_name; } class STMATRIX_NAME { defvar name = "llvm.nvvm.stmatrix.sync.aligned" # "." # Frag.geom # "." # Frag.frag # !if(Trans, ".trans", "") # "." # Frag.ptx_elt_type ; string intr_name = IntrinsicName.intr_name; string record_name = IntrinsicName.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 Geom, list TypeA, list TypeB, list TypeC, list TypeD, bit IsSparse = false> { list> ret = !foldl([]>, Geom, t1, geom, !listconcat(t1, !foldl([]>, TypeA, t2, type_a, !listconcat(t2, !foldl([]>, !if(!size(TypeB), TypeB, [type_a]), t3, type_b, !listconcat(t3, !foldl([]>, TypeC, t4, type_c, !listconcat(t4, !foldl([]>, !if(!size(TypeD), TypeD, [type_c]), t5, type_d, !listconcat(t5, [[WMMA_REGS, WMMA_REGS, WMMA_REGS, WMMA_REGS]])))))))))); // Debugging aid for readable representation of the list above. list> ops = !foreach(x, ret, [x[0].gft, x[1].gft, x[2].gft, x[3].gft]); } class MMA_LDST_OPS Geom, list Frags, list Types> { list ret = !foldl([], Geom, t1, geom, !listconcat(t1, !foldl([], Frags, t2, frag, !listconcat(t2, !foldl([], Types, t3, type, !listconcat(t3, [WMMA_REGS])))))); // Debugging aid for readable representation of the list above. list ops = !foreach(x, ret, x.gft); } class LDMATRIX_OPS Geom, list Frags, list Types> { list ret = !foldl([], Geom, t1, geom, !listconcat(t1, !foldl([], Frags, t2, frag, !listconcat(t2, !foldl([], Types, t3, type, !listconcat(t3, [WMMA_REGS])))))); // Debugging aid for readable representation of the list above. list ops = !foreach(x, ret, x.gft); } class STMATRIX_OPS Geom, list Frags, list Types> { list ret = !foldl([], Geom, t1, geom, !listconcat(t1, !foldl([], Frags, t2, frag, !listconcat(t2, !foldl([], Types, t3, type, !listconcat(t3, [WMMA_REGS])))))); // Debugging aid for readable representation of the list above. list 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> tf32_wmma_ops = MMA_OPS< ["m16n16k8"], ["tf32"], [], ["f32"], []>.ret; list> bf16_wmma_ops = MMA_OPS< ["m16n16k16", "m32n8k16", "m8n32k16"], ["bf16"], [], ["f32"], []>.ret; list> f64_wmma_ops = MMA_OPS< ["m8n8k4"], ["f64"], [], ["f64"], []>.ret; list> fp_wmma_ops = MMA_OPS< ["m16n16k16", "m32n8k16", "m8n32k16"], ["f16"], [], ["f16", "f32"], ["f16", "f32"]>.ret; list> int_wmma_ops = MMA_OPS< ["m16n16k16", "m32n8k16", "m8n32k16"], ["s8", "u8"], [], ["s32"], []>.ret; list> subint_wmma_ops = MMA_OPS< ["m8n8k32"], ["s4", "u4"], [], ["s32"], []>.ret; list> bit_wmma_ops = MMA_OPS< ["m8n8k128"], ["b1"], [], ["s32"], []>.ret; list> 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> tf32_mma_ops = MMA_OPS< ["m16n8k4", "m16n8k8"], ["tf32"], [], ["f32"], []>.ret; list> bf16_mma_ops = MMA_OPS< ["m16n8k16", "m16n8k8"], ["bf16"], [], ["f32"], []>.ret; list> f64_mma_ops = MMA_OPS< ["m8n8k4", "m16n8k4", "m16n8k8", "m16n8k16"], ["f64"], [], ["f64"], []>.ret; list> fp_mma_ops = MMA_OPS< ["m8n8k4", "m16n8k8", "m16n8k16"], ["f16"], [], ["f16", "f32"], ["f16", "f32"]>.ret; list> 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> fp8_mma_ops = MMA_OPS< ["m16n8k16"], ["e4m3", "e5m2"], ["e4m3", "e5m2"], ["f16", "f32"], ["f16", "f32"]>.ret; // it also contains e4m3/e5m2 from fp8 variants list> f8f6f4_mma_ops = MMA_OPS< ["m16n8k32"], ["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"], ["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"], ["f16", "f32"], ["f16", "f32"]>.ret; list> subint_mma_ops = MMA_OPS< ["m8n8k32", "m16n8k32", "m16n8k64"], ["s4", "u4"], ["s4", "u4"], ["s32"], []>.ret; list> bit_mma_ops = MMA_OPS< ["m8n8k128", "m16n8k128", "m16n8k256"], ["b1"], [], ["s32"], []>.ret; list> 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> mxf4_mma_ops = MMA_OPS< ["m16n8k64"], ["e2m1"], ["e2m1"], ["f32"], ["f32"] >.ret; list> mxf8f6f4_mma_ops = MMA_OPS< ["m16n8k32"], ["e2m1", "e2m3", "e3m2", "e5m2", "e4m3"], ["e2m1", "e2m3", "e3m2", "e5m2", "e4m3"], ["f32"], ["f32"] >.ret; list> all_mma_block_scale_ops = !listconcat( mxf4_mma_ops, mxf8f6f4_mma_ops); list> bf16_mma_sp_ops = MMA_OPS< ["m16n8k16", "m16n8k32"], ["bf16"], [], ["f32"], [], true>.ret; list> tf32_mma_sp_ops = MMA_OPS< ["m16n8k8", "m16n8k16"], ["tf32"], [], ["f32"], [], true>.ret; list> fp_mma_sp_ops = MMA_OPS< ["m16n8k16", "m16n8k32"], ["f16"], [], ["f16", "f32"], ["f16", "f32"], true>.ret; list> fp8_mma_sp_ops = MMA_OPS< ["m16n8k64"], ["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"], ["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"], ["f16", "f32"], ["f16", "f32"], true>.ret; list> subint_mma_sp_ops = MMA_OPS< ["m16n8k64", "m16n8k128"], ["s4", "u4"], ["s4", "u4"], ["s32"], [], true>.ret; list> int_mma_sp_ops = MMA_OPS< ["m16n8k32", "m16n8k64"], ["s8", "u8"], ["s8", "u8"], ["s32"], [], true>.ret; list> 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> mxf4xx_mma_sp_ops = MMA_OPS< ["m16n8k128"], ["e2m1"], ["e2m1"], ["f32"], [], true>.ret; list> mxf8f6f4_mma_sp_ops = MMA_OPS< ["m16n8k64"], ["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"], ["e4m3", "e5m2", "e3m2", "e2m3", "e2m1"], ["f32"], [], true>.ret; list> all_mma_sp_block_scale_ops = !listconcat( mxf4xx_mma_sp_ops, mxf8f6f4_mma_sp_ops); list ldst_ab_ops = MMA_LDST_OPS< ["m16n16k16", "m32n8k16", "m8n32k16"], ["a", "b"], ["f16", "u8", "s8", "bf16"]>.ret; list ldst_cd_ops = MMA_LDST_OPS< ["m16n16k16", "m32n8k16", "m8n32k16"], ["c", "d"], ["f16", "f32", "s32"]>.ret; list ldst_tf32_ab_ops = MMA_LDST_OPS< ["m16n16k8"], ["a", "b"], ["tf32"]>.ret; list ldst_tf32_cd_ops = MMA_LDST_OPS< ["m16n16k8"], ["c", "d"], ["f32"]>.ret; list ldst_f64_abcd_ops = MMA_LDST_OPS< ["m8n8k4"], ["a", "b", "c", "d"], ["f64"]>.ret; list ldst_subint_ab_ops = MMA_LDST_OPS< ["m8n8k32"], ["a", "b"], ["s4","u4"]>.ret; list ldst_bit_ab_ops = MMA_LDST_OPS< ["m8n8k128"], ["a", "b"], ["b1"]>.ret; list ldst_subint_cd_ops = MMA_LDST_OPS< ["m8n8k32", "m8n8k128"], ["c", "d"], ["s32"]>.ret; list 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 all_ld_ops = !filter(op, all_ldst_ops, !ne(op.frag, "d")); list all_st_ops = !filter(op, all_ldst_ops, !eq(op.frag, "d")); list ldmatrix_b16_ops = LDMATRIX_OPS< ["m8n8"], ["x1", "x2", "x4"], ["b16"]>.ret; list ldmatrix_geom_m16n16_ops = LDMATRIX_OPS< ["m16n16"], ["x1", "x2"], ["b8", "b8x16.b6x16_p32", "b8x16.b4x16_p64"]>.ret; list ldmatrix_geom_m8n16_ops = LDMATRIX_OPS< ["m8n16"], ["x1", "x2", "x4"], ["b8x16.b6x16_p32", "b8x16.b4x16_p64"]>.ret; list stmatrix_b16_ops = STMATRIX_OPS< ["m8n8"], ["x1", "x2", "x4"], ["b16"]>.ret; list stmatrix_b8_ops = STMATRIX_OPS< ["m16n8"], ["x1", "x2", "x4"], ["b8"]>.ret; list all_ldmatrix_ops = !listconcat(ldmatrix_b16_ops, ldmatrix_geom_m16n16_ops, ldmatrix_geom_m8n16_ops); list 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 { 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 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 frags> { list 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 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 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 { 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 { 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 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 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 { 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 RetTy = !if(return_pred, [OpType, llvm_i1_ty], [OpType]); list 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 { 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(ElemType)>, !eq(veclen, 2): LLVMType("v"#2#ElemType)>, !eq(veclen, 4): LLVMType("v"#4#ElemType)>, !eq(veclen, 8): LLVMType("v"#8#ElemType)>, !eq(veclen, 16): LLVMType("v"#16#ElemType)>, !eq(veclen, 32): LLVMType("v"#32#ElemType)>, !eq(veclen, 64): LLVMType("v"#64#ElemType)>, !eq(veclen, 128): LLVMType("v"#128#ElemType)>, true : llvm_void_ty); } class NVVM_TCGEN05_MMA_BASE { LLVMType a_operand_type = !if(!eq(Space, "tensor"), llvm_tmem_ptr_ty, llvm_i64_ty); list 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 common_intr_props = !listconcat( [IntrArgMemOnly, WriteOnly>], !if(!eq(Space, "tensor"), [ReadOnly>], []) ); string Prefix = "llvm.nvvm.tcgen05.mma"; string SpSpaceKindStr = !if(IsSparse, ".sp", "") # "." # Space # !if(!ne(Kind, ""), "." # Kind, ""); } class NVVM_TCGEN05_MMA: NVVM_TCGEN05_MMA_BASE { string name = Prefix # SpSpaceKindStr # !if(IsScaleInputD, ".scale_d", "") # !if(IsAShift, ".ashift", ""); string intr_name = IntrinsicName.intr_name; string record_name = IntrinsicName.record_name; } class NVVM_TCGEN05_MMA_BLOCKSCALE: NVVM_TCGEN05_MMA_BASE { string name = Prefix # SpSpaceKindStr # ".block_scale" # ScaleVecSize; string intr_name = IntrinsicName.intr_name; string record_name = IntrinsicName.record_name; } class NVVM_TCGEN05_MMA_WS: NVVM_TCGEN05_MMA_BASE { string name = Prefix # ".ws" # SpSpaceKindStr # !if(IsZeroColMask, ".zero_col_mask", ""); string intr_name = IntrinsicName.intr_name; string record_name = IntrinsicName.record_name; } class NVVM_TCGEN05_MMA_DISABLE_OUTPUT_LANE: NVVM_TCGEN05_MMA_BASE { string name = Prefix # SpSpaceKindStr # !if(IsScaleInputD, ".scale_d", "") # ".disable_output_lane.cg" # CtaGroup # !if(IsAShift, ".ashift", ""); string intr_name = IntrinsicName.intr_name; string record_name = IntrinsicName.record_name; } class NVVM_TCGEN05_MMA_BLOCKSCALE_SUPPORTED { 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 types> { string Name = name; list 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 { assert !eq(!substr(NAME, 0, !size("int_nvvm_")), "int_nvvm_"), "NVVMBuiltin must be a NVVM intrinsic starting with 'int_nvvm_'"; } class NVVMPureIntrinsic ret_types, list param_types = [], list intr_properties = [], string name = ""> : DefaultAttrsIntrinsic {} 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>]>; // // 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>]>; } } // // 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>]>; // // 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 ret_types = [], list extra_param_types = [], bit has_all_variant = true> { let IntrProperties = [IntrConvergent, IntrNoCallback] in { foreach align = ["", "_aligned"] in { def align # _count : Intrinsic; if has_all_variant then def align # _all : Intrinsic; } } } // 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; // 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>, Range, 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>, NoAlias>, WriteOnly>, ReadOnly>] 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>]>; 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>]>; def int_nvvm_cp_async_bulk_wait_group_read : Intrinsic<[], [llvm_i32_ty], [ImmArg>]>; // 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>, NoCapture>]>; 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], []); def int_nvvm_mbarrier_ # op # "_" # suffix: Intrinsic; def int_nvvm_mbarrier_ # op # "_relaxed_" # suffix : Intrinsic; } // 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], []); 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>, IntrNoCallback]>; def int_nvvm_mbarrier_ # tmp_op # "_relaxed_" # suffix : Intrinsic<[llvm_i1_ty], args, [IntrConvergent, NoCapture>, 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>] 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>, NoUndef]>; // 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>]>; // // 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], []); defvar base_args = !listconcat(addr_args, array_args); def int_nvvm_tex # mode # _1d # array # _ # vec.Name # _s32 : Intrinsic; def int_nvvm_tex # mode # _1d # array # _ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _1d # array # _level_ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _1d # array # _grad_ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _2d # array # _ # vec.Name # _s32 : Intrinsic; def int_nvvm_tex # mode # _2d # array # _ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _2d # array # _level_ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _2d # array # _grad_ # vec.Name # _f32 : Intrinsic; if !not(is_array) then { def int_nvvm_tex # mode # _3d_ # vec.Name # _s32 : Intrinsic; def int_nvvm_tex # mode # _3d_ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _3d_level_ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _3d_grad_ # vec.Name # _f32 : Intrinsic; } def int_nvvm_tex # mode # _cube # array # _ # vec.Name # _f32 : Intrinsic; def int_nvvm_tex # mode # _cube # array # _level_ # vec.Name # _f32 : Intrinsic; if is_unified then def int_nvvm_tex # mode # _cube # array # _grad_ # vec.Name # _f32 : Intrinsic; } // is_array foreach comp = ["r", "g", "b", "a"] in { def int_nvvm_tld4 # mode # _ # comp # _2d_ # vec.Name # _f32 : Intrinsic; } // 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], []) in { def int_nvvm_suld_1d_ # vec.Name # _ # clamp : Intrinsic; def int_nvvm_suld_1d_array_ # vec.Name # _ # clamp : Intrinsic; def int_nvvm_suld_2d_ # vec.Name # _ # clamp : Intrinsic; def int_nvvm_suld_2d_array_ # vec.Name # _ # clamp : Intrinsic; def int_nvvm_suld_3d_ # vec.Name # _ # clamp : Intrinsic; } } // 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 { let IntrProperties = [IntrNoCallback, IntrNoFree, IntrWriteMem] # !if(!ne(clamp, "trap"), [IntrWillReturn], []) 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; // 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 properties = [], string name = ""> : NVVMPureIntrinsic<[llvm_i32_ty], [], [NoUndef] # properties, !if(!eq(name, ""), "", !strconcat("llvm.nvvm.read.ptx.sreg.", name))>; class PTXReadSRegIntrinsic_r32 properties = []> : PTXReadSRegIntrinsicNB_r32, NVVMBuiltin; multiclass PTXReadSRegIntrinsic_v4i32> 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; } // Same, but without automatic clang builtins. It will be used for // registers that require particular GPU or PTX version. multiclass PTXReadSRegIntrinsicNB_v4i32> 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; } // 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 : Intrinsic<[ty], [], [IntrInaccessibleMemOnly, IntrNoCallback, IntrNoFree, IntrWillReturn, NoUndef]>, NVVMBuiltin; defvar MAX_BLOCK_ID_RANGE = [[Range], [Range], [Range], [Range]]; defvar MAX_BLOCK_NID_RANGE = [[Range], [Range], [Range], [Range]]; defvar MAX_GRID_ID_RANGE = [[Range], [Range], [Range], [Range]]; defvar MAX_GRID_NID_RANGE = [[Range], [Range], [Range], [Range]]; defm int_nvvm_read_ptx_sreg_tid : PTXReadSRegIntrinsic_v4i32; defm int_nvvm_read_ptx_sreg_ntid : PTXReadSRegIntrinsic_v4i32; def int_nvvm_read_ptx_sreg_laneid : PTXReadSRegIntrinsic_r32<[Range]>; 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; defm int_nvvm_read_ptx_sreg_nctaid : PTXReadSRegIntrinsic_v4i32; 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; def int_nvvm_read_ptx_sreg_clock64 : PTXReadNCSRegIntrinsic; def int_nvvm_read_ptx_sreg_globaltimer : PTXReadNCSRegIntrinsic; def int_nvvm_read_ptx_sreg_globaltimer_lo : PTXReadNCSRegIntrinsic; foreach i = 0...4 in def int_nvvm_read_ptx_sreg_pm # i : PTXReadNCSRegIntrinsic; def int_nvvm_read_ptx_sreg_warpsize : PTXReadSRegIntrinsic_r32<[Range]>; 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; defm int_nvvm_read_ptx_sreg_nclusterid : PTXReadSRegIntrinsicNB_v4i32; defm int_nvvm_read_ptx_sreg_cluster_ctaid : PTXReadSRegIntrinsicNB_v4i32; defm int_nvvm_read_ptx_sreg_cluster_nctaid : PTXReadSRegIntrinsicNB_v4i32; 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; def int_nvvm_read_ptx_sreg_reserved_smem_offset_end : PTXReadSRegIntrinsicNB_r32; def int_nvvm_read_ptx_sreg_reserved_smem_offset_cap : PTXReadSRegIntrinsicNB_r32; def int_nvvm_read_ptx_sreg_reserved_smem_offset_0 : PTXReadSRegIntrinsicNB_r32; def int_nvvm_read_ptx_sreg_reserved_smem_offset_1 : PTXReadSRegIntrinsicNB_r32; def int_nvvm_read_ptx_sreg_total_smem_size : PTXReadSRegIntrinsicNB_r32; def int_nvvm_read_ptx_sreg_aggr_smem_size : PTXReadSRegIntrinsicNB_r32; def int_nvvm_read_ptx_sreg_dynamic_smem_size : PTXReadSRegIntrinsicNB_r32; // // 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; if i.withGccBuiltin then def i.Name : NVVMBuiltin, Intrinsic; else def i.Name : Intrinsic; } } } } } // // 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>], "llvm.nvvm.wgmma.wait_group.sync.aligned">; // // WMMA instructions // // WMMA.LOAD class NVVM_WMMA_LD : Intrinsic>, NoCapture>], WMMA_NAME_LDST<"load", Frag, Layout, WithStride>.intr_name>; // WMMA.STORE.D class NVVM_WMMA_ST : Intrinsic<[], !listconcat( [llvm_anyptr_ty], Frag.regs, !if(WithStride, [llvm_i32_ty], [])), [IntrWriteMem, IntrArgMemOnly, IntrNoCallback, WriteOnly>, NoCapture>], 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.ret then def WMMA_NAME_LDST<"load", frag, layout, stride>.record_name : NVVM_WMMA_LD; foreach frag = NVVM_MMA_OPS.all_st_ops in if NVVM_WMMA_LDST_SUPPORTED.ret then def WMMA_NAME_LDST<"store", frag, layout, stride>.record_name : NVVM_WMMA_ST; } } // WMMA.MMA class NVVM_MMA : Intrinsic; 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.ret in { if NVVM_WMMA_SUPPORTED.ret then { def WMMA_NAME.record_name : NVVM_MMA; } } // 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.ret in { foreach kind = ["", "kind::f8f6f4"] in { if NVVM_MMA_SUPPORTED.ret then { def MMA_NAME.record_name : NVVM_MMA; } } // kind } // b1op } // op } // satf } // layout_b } // layout_a class NVVM_MMA_BLOCK_SCALE : Intrinsic; 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.ret then { def MMA_BLOCK_SCALE_NAME.record_name : NVVM_MMA_BLOCK_SCALE; } } // op } // stype } // scale_vec_size } // kind // MMA.SP class NVVM_MMA_SP : Intrinsic { 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>, Range, 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.ret then { def MMA_SP_NAME.record_name : NVVM_MMA_SP; } } // op } // satf } // kind } // metadata // MMA.SP BLOCK SCALE class NVVM_MMA_SP_BLOCK_SCALE : Intrinsic { 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>, Range, 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.ret then { def MMA_SP_BLOCK_SCALE_NAME.record_name : NVVM_MMA_SP_BLOCK_SCALE; } } // op } // stype } // scale_vec_size } // kind // LDMATRIX class NVVM_LDMATRIX : Intrinsic>, NoCapture>], LDMATRIX_NAME.intr_name>; foreach transposed = [0, 1] in { foreach frag = NVVM_MMA_OPS.all_ldmatrix_ops in { if NVVM_LDMATRIX_SUPPORTED.ret then { def LDMATRIX_NAME.record_name : NVVM_LDMATRIX; } } } // STMATRIX class NVVM_STMATRIX : Intrinsic<[], !listconcat([llvm_anyptr_ty], Frag.regs), [IntrWriteMem, IntrArgMemOnly, IntrNoCallback, IntrConvergent, WriteOnly>, NoCapture>], STMATRIX_NAME.intr_name>; foreach transposed = [0, 1] in { foreach frag = NVVM_MMA_OPS.all_stmatrix_ops in { if NVVM_STMATRIX_SUPPORTED.ret then { def STMATRIX_NAME.record_name : NVVM_STMATRIX; } } } // 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>] 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>] 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], "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>, Range, 24, 257>]>; // Exit def int_nvvm_exit : NVVMBuiltin, Intrinsic<[], [], [IntrConvergent, IntrInaccessibleMemOnly, IntrNoReturn]>; class DefaultAttrsIntrinsicFlags ret_types, list param_types, list flags, list intr_properties, string name = ""> : DefaultAttrsIntrinsic< ret_types, !listconcat(param_types, flags), !listconcat(intr_properties, !foreach(i, !range(flags), ImmArg>)), 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>, ReadOnly>]>; // 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>, ReadOnly>]>; } } // 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>, ReadOnly>]>; // 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>, ReadOnly>, // Allowed values for cta_group are {0,1,2} i.e [0, 3). Range, 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>]>; 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>, ReadOnly>]>; } } // 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>, ReadOnly>, // Allowed values for cta_group are {0,1,2} i.e [0, 3). Range, 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>, ReadOnly>]>; // 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>]>; // Intrinsics for Prefetch and Prefetchu let IntrProperties = [IntrArgMemOnly, ReadOnly>, NoCapture>] 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>, NoCapture>, ImmArg>] 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>, ImmArg>, 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>, ReadOnly>]>; // 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>, ReadOnly>]>; // 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>, ReadOnly>]>; // 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>, ReadOnly>]>; // 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>, ReadOnly>, ImmArg>]>; // 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>, ReadOnly>]>; 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>, NoCapture>]>; def int_nvvm_tcgen05_alloc_shared_ # cta_group : Intrinsic<[], [llvm_shared_ptr_ty, // dst_ptr llvm_i32_ty], // num_columns [IntrConvergent, IntrInaccessibleMemOrArgMemOnly, WriteOnly>, NoCapture>]>; def int_nvvm_tcgen05_dealloc_ # cta_group : Intrinsic<[], [llvm_tmem_ptr_ty, // tmem_addr llvm_i32_ty], // num_columns [IntrConvergent, IntrArgMemOnly, NoCapture>]>; 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>]>; def int_nvvm_tcgen05_commit_shared_ # cta_group : Intrinsic<[], [llvm_shared_ptr_ty], // mbar_ptr [IntrConvergent, IntrInaccessibleMemOrArgMemOnly, NoCapture>]>; def int_nvvm_tcgen05_commit_mc_ # cta_group : Intrinsic<[], [llvm_ptr_ty, llvm_i16_ty], // mbar_ptr, cta_mask [IntrConvergent, IntrInaccessibleMemOrArgMemOnly, NoCapture>]>; def int_nvvm_tcgen05_commit_mc_shared_ # cta_group : Intrinsic<[], [llvm_shared_ptr_ty, llvm_i16_ty], // mbar_ptr, cta_mask [IntrConvergent, IntrInaccessibleMemOrArgMemOnly, NoCapture>]>; def int_nvvm_tcgen05_shift_down_ # cta_group : Intrinsic<[], [llvm_tmem_ptr_ty], // tmem_addr [IntrConvergent, IntrArgMemOnly, NoCapture>]>; } // 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; def intrinsic_name.record_name : Intrinsic<[], [llvm_tmem_ptr_ty, // tmem_addr llvm_i64_ty], // smem descriptor [IntrConvergent, IntrInaccessibleMemOrArgMemOnly, NoCapture>], intrinsic_name.intr_name>; } } } // Tcgen05 ld intrinsics class NVVM_TCGEN05_LD : Intrinsic<[NVVM_TCGEN05_LDST_ACCESS_SIZE.type], !listconcat([llvm_tmem_ptr_ty], !if(!eq(Shape, "16x32bx2"), [llvm_i64_ty], []), [llvm_i1_ty]), !listconcat([IntrConvergent, IntrArgMemOnly, NoCapture>], !if(!eq(Shape, "16x32bx2"), [ImmArg>, ImmArg>], [ImmArg>]))>; // Tcgen05 ld.red intrinsics class NVVM_TCGEN05_LD_RED : DefaultAttrsIntrinsicFlags< [NVVM_TCGEN05_LDST_ACCESS_SIZE.type, LLVMType(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>]>; // Tcgen05 st intrinsics class NVVM_TCGEN05_ST : Intrinsic<[], !listconcat([llvm_tmem_ptr_ty], !if(!eq(Shape, "16x32bx2"), [llvm_i64_ty], []), [NVVM_TCGEN05_LDST_ACCESS_SIZE.type], [llvm_i1_ty]), !listconcat([IntrConvergent, IntrArgMemOnly, NoCapture>], !if(!eq(Shape, "16x32bx2"), [ImmArg>, ImmArg>], [ImmArg>]))>; // tcgen05 ld/st intrinsics foreach shape = ["16x64b", "16x128b", "16x256b", "32x32b", "16x32bx2"] in { foreach num = 0...8 in { if NVVM_TCGEN05_LDST_ACCESS_SIZE.valid then { def int_nvvm_tcgen05_ld_ # shape # _x # !shl(1, num) : NVVM_TCGEN05_LD; def int_nvvm_tcgen05_st_ # shape # _x # !shl(1, num) : NVVM_TCGEN05_ST; } } } // 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.valid then { def IntrinsicName<"llvm.nvvm.tcgen05.ld.red." # shape # ".x" # !shl(1, num) # "." # ty>.record_name: NVVM_TCGEN05_LD_RED; } // valid } // ty } // num } // shape // // Bulk store intrinsics // let IntrProperties = [IntrArgMemOnly, IntrWriteMem, WriteOnly>, NoCapture>, ImmArg>] 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; 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; defvar scale_d_imm_range = [ImmArg, Range]; // 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, 0, !if(!eq(scale_d, 1), 2, 4)>, // kind Range, 1, 3>, // cta_group Range, 0, !if(!eq(ashift, 1), 2, 4)> // collector_usage ] ); defvar intrinsic_properties = !if(is_target_intrinsic, !listconcat(base_properties, [ArgInfo, [ArgName<"kind">, ImmArgPrinter<"printTcgen05MMAKind">]>, ArgInfo, [ArgName<"cta_group">]>, ArgInfo, [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; defvar scale_d_imm_range = [ImmArg, Range]; defvar intrinsic_properties = !listconcat( mma.common_intr_props, !if(!eq(scale_d, 1), scale_d_imm_range, []), [Range, 0, !if(!eq(scale_d, 1), 2, 4)>, Range, 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; 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; defvar collector_usage = ArgIndex; if NVVM_TCGEN05_MMA_BLOCKSCALE_SUPPORTED.ret then { def mma.record_name : DefaultAttrsIntrinsicFlags<[], args, flags, !listconcat(mma.common_intr_props, [Range, Range]), 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; 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, 0, 4>, Range, 0, 4>, Range, 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>] 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>, IntrWriteMem, NoCapture>] 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>, NoCapture>, Range, 0, 16>, ArgInfo, [ArgName<"elemtype">, ImmArgPrinter<"printTensormapElemType">]>]>; def int_nvvm_tensormap_replace_interleave_layout : DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty], [IntrArgMemOnly, IntrWriteMem, ImmArg>, NoCapture>, Range, 0, 3>, ArgInfo, [ArgName<"interleave_layout">, ImmArgPrinter<"printTensormapInterleaveLayout">]>]>; def int_nvvm_tensormap_replace_swizzle_mode : DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty], [IntrArgMemOnly, IntrWriteMem, ImmArg>, NoCapture>, Range, 0, 5>, ArgInfo, [ArgName<"swizzle_mode">, ImmArgPrinter<"printTensormapSwizzleMode">]>]>; def int_nvvm_tensormap_replace_swizzle_atomicity : DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty], [IntrArgMemOnly, IntrWriteMem, ImmArg>, NoCapture>, Range, 0, 4>, ArgInfo, [ArgName<"swizzle_atomicity">, ImmArgPrinter<"printTensormapSwizzleAtomicity">]>]>; def int_nvvm_tensormap_replace_fill_mode : DefaultAttrsIntrinsic<[], [llvm_anyptr_ty, llvm_i32_ty], [IntrArgMemOnly, IntrWriteMem, ImmArg>, NoCapture>, Range, 0, 2>, ArgInfo, [ArgName<"fill_mode">, ImmArgPrinter<"printTensormapFillMode">]>]>; } // let TargetPrefix = "nvvm"