| # User Guide for NVPTX Back-end | |
| ## Introduction | |
| To support GPU programming, the NVPTX back-end supports a subset of LLVM IR | |
| along with a defined set of conventions used to represent GPU programming | |
| concepts. This document provides an overview of the general usage of the back- | |
| end, including a description of the conventions used and the set of accepted | |
| LLVM IR. | |
| :::{note} | |
| This document assumes a basic familiarity with CUDA and the PTX | |
| assembly language. Information about the CUDA Driver API and the PTX assembly | |
| language can be found in the [CUDA documentation](http://docs.nvidia.com/cuda/index.html). | |
| ::: | |
| ## Conventions | |
| ### Marking Functions as Kernels | |
| In PTX, there are two types of functions: *device functions*, which are only | |
| callable by device code, and *kernel functions*, which are callable by host | |
| code. By default, the back-end will emit device functions. The `ptx_kernel` | |
| calling convention is used to declare a function as a kernel function. | |
| The following example shows a kernel function calling a device function in LLVM | |
| IR. The function `@my_kernel` is callable from host code, but `@my_fmad` is | |
| not. | |
| ```llvm | |
| define float @my_fmad(float %x, float %y, float %z) { | |
| %mul = fmul float %x, %y | |
| %add = fadd float %mul, %z | |
| ret float %add | |
| } | |
| define ptx_kernel void @my_kernel(ptr %ptr) { | |
| %val = load float, ptr %ptr | |
| %ret = call float @my_fmad(float %val, float %val, float %val) | |
| store float %ret, ptr %ptr | |
| ret void | |
| } | |
| ``` | |
| When compiled, the PTX kernel functions are callable by host-side code. | |
| ### Parameter Attributes | |
| `"nvvm.grid_constant"` | |
| : This attribute may be attached to a `byval` parameter of a kernel function | |
| to indicate that the parameter should be lowered as a direct reference to | |
| the grid-constant memory of the parameter, as opposed to a copy of the | |
| parameter in local memory. Writing to a grid-constant parameter is | |
| undefined behavior. Unlike a normal `byval` parameter, the address of a | |
| grid-constant parameter is not unique to a given function invocation but | |
| instead is shared by all kernels in the grid. | |
| (nvptx-fnattrs)= | |
| ### Function Attributes | |
| `"nvvm.maxclusterrank"="<n>"` | |
| : This attribute specifies the maximum number of blocks per cluster. Must be | |
| non-zero. Only supported for Hopper+. | |
| `"nvvm.minctasm"="<n>"` | |
| : This indicates a hint/directive to the compiler/driver, asking it to put at | |
| least these many CTAs on an SM. | |
| `"nvvm.maxnreg"="<n>"` | |
| : This attribute indicates the maximum number of registers to be used for the | |
| kernel function. | |
| `"nvvm.maxntid"="<x>[,<y>[,<z>]]"` | |
| : This attribute declares the maximum number of threads in the thread block | |
| (CTA). The maximum number of threads is the product of the maximum extent in | |
| each dimension. Exceeding the maximum number of threads results in a runtime | |
| error or kernel launch failure. | |
| `"nvvm.reqntid"="<x>[,<y>[,<z>]]"` | |
| : This attribute declares the exact number of threads in the thread block | |
| (CTA). The number of threads is the product of the value in each dimension. | |
| Specifying a different CTA dimension at launch will result in a runtime | |
| error or kernel launch failure. | |
| `"nvvm.cluster_dim"="<x>[,<y>[,<z>]]"` | |
| : This attribute declares the number of thread blocks (CTAs) in the cluster. | |
| The total number of CTAs is the product of the number of CTAs in each | |
| dimension. Specifying a different cluster dimension at launch will result in | |
| a runtime error or kernel launch failure. Only supported for Hopper+. | |
| `"nvvm.blocksareclusters"` | |
| : This attribute implies that the grid launch configuration for the | |
| corresponding kernel function is specifying the number of clusters instead | |
| of the number of thread blocks. This attribute is only allowed for kernel | |
| functions and requires `nvvm.reqntid` and `nvvm.cluster_dim` attributes. | |
| (address-spaces)= | |
| ### Address Spaces | |
| The NVPTX back-end uses the following address space mapping: | |
| | Address Space | Memory Space | | |
| | ------------- | -------------- | | |
| | 0 | Generic | | |
| | 1 | Global | | |
| | 2 | Internal Use | | |
| | 3 | Shared | | |
| | 4 | Constant | | |
| | 5 | Local | | |
| | 7 | Shared Cluster | | |
| Every global variable and pointer type is assigned to one of these address | |
| spaces, with 0 being the default address space. Intrinsics are provided which | |
| can be used to convert pointers between the generic and non-generic address | |
| spaces. | |
| As an example, the following IR will define an array `@g` that resides in | |
| global device memory. | |
| ```llvm | |
| @g = internal addrspace(1) global [4 x i32] [ i32 0, i32 1, i32 2, i32 3 ] | |
| ``` | |
| LLVM IR functions can read and write to this array, and host-side code can | |
| copy data to it by name with the CUDA Driver API. | |
| Note that since address space 0 is the generic space, it is illegal to have | |
| global variables in address space 0. Address space 0 is the default address | |
| space in LLVM, so the `addrspace(N)` annotation is *required* for global | |
| variables. | |
| ### Triples | |
| The NVPTX target uses the module triple to select between 32/64-bit code | |
| generation and the driver-compiler interface to use. The triple architecture | |
| can be one of `nvptx` (32-bit PTX) or `nvptx64` (64-bit PTX). The | |
| operating system should be one of `cuda` or `nvcl`, which determines the | |
| interface used by the generated code to communicate with the driver. Most | |
| users will want to use `cuda` as the operating system, which makes the | |
| generated PTX compatible with the CUDA Driver API. | |
| Example: 32-bit PTX for CUDA Driver API: `nvptx-nvidia-cuda` | |
| Example: 64-bit PTX for CUDA Driver API: `nvptx64-nvidia-cuda` | |
| (nvptx-arch-hierarchy)= | |
| ## NVPTX Architecture Hierarchy and Ordering | |
| GPU architectures: sm_2Y/sm_3Y/sm_5Y/sm_6Y/sm_7Y/sm_8Y/sm_9Y/sm_10Y/sm_12Y | |
| ('Y' represents version within the architecture). The architectures have name of | |
| the form `sm_XYz` where: | |
| - `X` represent the generation number | |
| - `Y` represents the version within the architecture, and | |
| - `z` represents the optional feature suffix. | |
| If `X1Y1 <= X2Y2`, then GPU capabilities of `sm_X1Y1` are included in | |
| `sm_X2Y2`. For example, take `sm_90` (9 represents `X`, 0 represents | |
| `Y`, and no feature suffix) and `sm_103` architectures (10 represents `X`, | |
| 3 represents `Y`, and no feature suffix). Since 90 <= 103, `sm_90` is | |
| compatible with `sm_103`. | |
| The family-specific variants have `f` feature suffix and they follow | |
| following order: | |
| `sm_X{Y2}f > sm_X{Y1}f` iff `Y2 > Y1` | |
| `sm_XY{f} > sm_{XY}{}` | |
| For example, take `sm_100f` (10 represents `X`, 0 represents `Y`, and | |
| `f` represents `z`) and `sm_103f` (10 represents `X`, 3 represents | |
| `Y`, and `f` represents `z`) architecture variants. Since `Y1 < Y2`, | |
| `sm_100f` is compatible with `sm_103f`. Similarly based on the second rule, | |
| `sm_90` is compatible with `sm_103f`. | |
| Some counter examples, take `sm_100f` and `sm_120f` (12 represents `X`, 0 | |
| represents `Y`, and `f` represents `z`) architecture variants. Since both | |
| belongs to different family i.e. `X1 != X2`, `sm_100f` is not compatible | |
| with `sm_120f`. | |
| The architecture-specific variants have `a` feature suffix and they follow | |
| following order: | |
| `sm_XY{a} > sm_XY{f} > sm_{XY}{}` | |
| For example, take `sm_103a` (10 represents `X`, 3 represents `Y`, and | |
| `a` represents `z`), `sm_103f`, and `sm_103` architecture variants. The | |
| `sm_103` is compatible with `sm_103a` and `sm_103f`, and `sm_103f` is | |
| compatible with `sm_103a`. | |
| Encoding := Arch * 10 + 2 (for 'f') + 1 (for 'a') | |
| Arch := X * 10 + Y | |
| For example, `sm_103f` is encoded as 1032 (103 * 10 + 2) and `sm_103a` is | |
| encoded as 1033 (103 * 10 + 2 + 1). | |
| This encoding allows simple partial ordering of the architectures. | |
| - Compare Family and Arch by dividing FullSMVersion by 100 and 10 | |
| respectively before the comparison. | |
| - Compare within the family by comparing FullSMVersion, given both belongs to | |
| the same family. | |
| - Detect `a` variants by checking FullSMVersion & 1. | |
| (nvptx-intrinsics)= | |
| ## NVPTX Intrinsics | |
| ### Reading PTX Special Registers | |
| #### '`llvm.nvvm.read.ptx.sreg.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.tid.y() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.tid.z() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.ntid.x() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.ntid.y() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.ntid.z() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.x() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.y() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.z() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.nctaid.x() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.nctaid.y() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.nctaid.z() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.warpsize() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.read.ptx.sreg.*`' intrinsics provide access to the PTX | |
| special registers, in particular the kernel launch bounds. These registers | |
| map in the following way to CUDA builtins: | |
| | CUDA Builtin | PTX Special Register Intrinsic | | |
| | ------------ | ----------------------------------- | | |
| | `threadId` | `@llvm.nvvm.read.ptx.sreg.tid.*` | | |
| | `blockIdx` | `@llvm.nvvm.read.ptx.sreg.ctaid.*` | | |
| | `blockDim` | `@llvm.nvvm.read.ptx.sreg.ntid.*` | | |
| | `gridDim` | `@llvm.nvvm.read.ptx.sreg.nctaid.*` | | |
| #### '`llvm.nvvm.read.ptx.sreg.*_smem_size`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.read.ptx.sreg.total_smem_size() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.aggr_smem_size() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.dynamic_smem_size() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.read.ptx.sreg.total_smem_size`' intrinsic reads the PTX | |
| special register that holds the total amount of shared memory allocated per CTA | |
| for the kernel at launch. | |
| The reported value includes both statically allocated and dynamically requested | |
| shared memory, but excludes any shared memory reserved for system use. The size | |
| is expressed in units of the architecture-specific shared memory allocation | |
| granularity. For targets sm_8x and newer, this granularity is 128 bytes. | |
| The '`aggr_smem_size`' variant returns the aggregate shared memory size, | |
| including the portion reserved for system software use. | |
| The '`dynamic_smem_size`' variant returns the amount of dynamic shared | |
| memory allocated per CTA for the kernel at launch time. | |
| #### '`llvm.nvvm.read.ptx.sreg.reserved_smem_offset_*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.read.ptx.sreg.reserved_smem_offset_begin() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.reserved_smem_offset_end() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.reserved_smem_offset_cap() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.reserved_smem_offset_0() | |
| declare i32 @llvm.nvvm.read.ptx.sreg.reserved_smem_offset_1() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.read.ptx.sreg.reserved_smem_offset_*`' intrinsics provide | |
| access to PTX special registers that hold information about reserved shared | |
| memory offsets. | |
| The '`reserved_smem_offset_begin`' intrinsic reads the start offset of | |
| the reserved shared memory region. | |
| The '`reserved_smem_offset_end`' intrinsic reads the end offset of the | |
| reserved shared memory region. | |
| The '`reserved_smem_offset_cap`' intrinsic reads the capacity limit of | |
| the reserved shared memory region. | |
| The '`reserved_smem_offset_0`' and '`reserved_smem_offset_1`' intrinsics | |
| read additional offsets in the reserved shared memory region. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#special-registers-reserved-smem). | |
| ### Barriers | |
| #### '`llvm.nvvm.barrier.cta.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.barrier.cta.sync.count(i32 %id, i32 %n) | |
| declare void @llvm.nvvm.barrier.cta.sync.all(i32 %id) | |
| declare void @llvm.nvvm.barrier.cta.sync.aligned.count(i32 %id, i32 %n) | |
| declare void @llvm.nvvm.barrier.cta.sync.aligned.all(i32 %id) | |
| declare void @llvm.nvvm.barrier.cta.arrive.count(i32 %id, i32 %n) | |
| declare void @llvm.nvvm.barrier.cta.arrive.aligned.count(i32 %id, i32 %n) | |
| declare i32 @llvm.nvvm.barrier.cta.red.popc.count(i32 %id, i32 %n, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.popc.all(i32 %id, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.popc.aligned.count(i32 %id, i32 %n, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.popc.aligned.all(i32 %id, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.and.count(i32 %id, i32 %n, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.and.all(i32 %id, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.and.aligned.count(i32 %id, i32 %n, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.and.aligned.all(i32 %id, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.or.count(i32 %id, i32 %n, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.or.all(i32 %id, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.or.aligned.count(i32 %id, i32 %n, i1 %pred) | |
| declare i32 @llvm.nvvm.barrier.cta.red.or.aligned.all(i32 %id, i1 %pred) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.barrier.cta.*`' family of intrinsics perform barrier | |
| synchronization and communication within a CTA. They can be used by the threads | |
| within the CTA for synchronization and communication. | |
| ##### Semantics: | |
| Operand %id specifies a logical barrier resource and must fall within the range | |
| 0 through 15. When present, operand %n specifies the number of threads | |
| participating in the barrier. When specifying a thread count, the value must be | |
| a multiple of the warp size. With the '`@llvm.nvvm.barrier.cta.sync.*`' | |
| variants, the '`.all`' suffix indicates that all threads in the CTA should | |
| participate in the barrier while the '`.count`' suffix indicates that only | |
| the threads specified by the %n operand should participate in the barrier. | |
| All forms of the '`@llvm.nvvm.barrier.cta.*`' intrinsic cause the executing | |
| thread to wait for all non-exited threads from its warp and then marks the | |
| warp's arrival at the barrier. In addition to signaling its arrival at the | |
| barrier, the '`@llvm.nvvm.barrier.cta.red.*`' and | |
| '`@llvm.nvvm.barrier.cta.sync.*`' intrinsics cause the executing thread to | |
| wait for non-exited threads of all other warps participating in the barrier to | |
| arrive. On the other hand, the '`@llvm.nvvm.barrier.cta.arrive.*`' intrinsic | |
| does not cause the executing thread to wait for threads of other participating | |
| warps. | |
| When a barrier completes, the waiting threads are restarted without delay, | |
| and the barrier is reinitialized so that it can be immediately reused. | |
| The '`@llvm.nvvm.barrier.cta.red.*`' intrinsics perform a reduction operation | |
| across threads. The %pred operands from all threads in the CTA are combined | |
| using the specified reduction operator. Once the barrier count is reached, the | |
| final value is returned in all threads waiting at the barrier. | |
| The reduction operations for '`@llvm.nvvm.barrier.cta.red.*`' are | |
| population-count ('`.popc`'), all-threads-true ('`.and`'), | |
| and any-thread-true ('`.or`'). The result of '`.popc`' is the number of | |
| threads with a true predicate, while '`.and`' and '`.or`' indicate if all | |
| the threads had a true predicate or if any of the threads had a true predicate. | |
| The '`@llvm.nvvm.barrier.cta.*`' intrinsic has an optional '`.aligned`' | |
| modifier to indicate textual alignment of the barrier. When specified, it | |
| indicates that all threads in the CTA will execute the same | |
| '`@llvm.nvvm.barrier.cta.*`' instruction. In conditionally executed code, an | |
| aligned '`@llvm.nvvm.barrier.cta.*`' instruction should only be used if it is | |
| known that all threads in the CTA evaluate the condition identically, otherwise | |
| behavior is undefined. | |
| ### MBarrier family of Intrinsics | |
| #### Overview: | |
| An `mbarrier` is a barrier created in shared memory that supports: | |
| - Synchronizing any subset of threads within a CTA. | |
| - One-way synchronization of threads across CTAs of a cluster. | |
| Threads can perform only `arrive` operations but not `*_wait` on an | |
| mbarrier located in shared::cluster space. | |
| - Waiting for completion of asynchronous memory operations initiated by a | |
| thread and making them visible to other threads. | |
| Unlike `bar{.cta}/barrier{.cta}` instructions which can access a limited | |
| number of barriers per CTA, `mbarrier` objects are user-defined and are | |
| only limited by the total shared memory size available. | |
| An mbarrier object is an opaque object in shared memory with an | |
| alignment of 8-bytes. It keeps track of: | |
| - Current phase of the mbarrier object | |
| - Count of pending arrivals for the current phase of the mbarrier object | |
| - Count of expected arrivals for the next phase of the mbarrier object | |
| - Count of pending asynchronous memory operations (or transactions) | |
| tracked by the current phase of the mbarrier object. This is also | |
| referred to as `tx-count`. The unit of `tx-count` is specified | |
| by the asynchronous memory operation (for example, | |
| `llvm.nvvm.cp.async.bulk.tensor.g2s.*`). | |
| The `phase` of an mbarrier object is the number of times the mbarrier | |
| object has been used to synchronize threads/track async operations. | |
| In each phase, threads perform: | |
| - arrive/expect-tx/complete-tx operations to progress the current phase. | |
| - test_wait/try_wait operations to check for completion of the current phase. | |
| An mbarrier object completes the current phase when: | |
| - The count of the pending arrivals has reached zero AND | |
| - The tx-count has reached zero. | |
| When an mbarrier object completes the current phase, below | |
| actions are performed `atomically`: | |
| - The mbarrier object transitions to the next phase. | |
| - The pending arrival count is reinitialized to the expected arrival count. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#parallel-synchronization-and-communication-instructions-mbarrier). | |
| #### '`llvm.nvvm.mbarrier.init`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.mbarrier.init.p0(ptr %addr, i32 %count, i32 immarg range(i32 0, 2) %layout) | |
| declare void @llvm.nvvm.mbarrier.init.p3(ptr addrspace(3) %addr, i32 %count, i32 immarg range(i32 0, 2) %layout) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.mbarrier.init.*`' intrinsics are used to initialize | |
| an mbarrier object located at `addr` with the value `count`. | |
| `count` is a 32-bit unsigned integer value and must be within | |
| the range [1...2^20-1]. During initialization: | |
| - The tx-count and the current phase of the mbarrier object are set to 0. | |
| - The expected and pending arrival counts are set to `count`. | |
| - `%layout` is an immediate argument that accepts only | |
| `0` (`layout::v0`) or `1` (`layout::v1`). | |
| ##### Semantics: | |
| The `addr` operand is overloaded and must point to either generic or | |
| shared::cta memory. When it is generic, the underlying address must fall | |
| within the shared::cta space, otherwise the behavior of this intrinsic | |
| is undefined. | |
| Performing `mbarrier.init` on a valid mbarrier object is undefined; | |
| use `mbarrier.inval` before reusing the memory for another mbarrier | |
| or any other purpose. | |
| An mbarrier object initialized with a particular layout must only be | |
| used with operations that support that layout; the layout of an existing | |
| mbarrier object can be queried with `llvm.nvvm.mbarrier.check_layout.*`. | |
| #### '`llvm.nvvm.mbarrier.check_layout`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i1 @llvm.nvvm.mbarrier.check_layout.p0(ptr %addr, i32 immarg range(i32 0, 2) %layout) | |
| declare i1 @llvm.nvvm.mbarrier.check_layout.p3(ptr addrspace(3) %addr, i32 immarg range(i32 0, 2) %layout) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.mbarrier.check_layout.*`' intrinsics test whether the | |
| mbarrier object at `addr` was initialized with the layout named by | |
| `%layout`. They return `true` when the layout matches and `false` | |
| otherwise. `%layout` is an immediate argument that accepts only | |
| `0` (`layout::v0`) or `1` (`layout::v1`). | |
| ##### Semantics: | |
| The `addr` operand is overloaded and must point to either generic or | |
| shared::cta memory. When it is generic, the underlying address must fall | |
| within the shared::cta space, otherwise the behavior of this intrinsic | |
| is undefined. It is expected that `addr` was previously initialized using | |
| `mbarrier.init`; otherwise, the behavior is undefined. | |
| #### '`llvm.nvvm.mbarrier.inval`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.mbarrier.inval(ptr %addr) | |
| declare void @llvm.nvvm.mbarrier.inval.shared(ptr addrspace(3) %addr) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.mbarrier.inval.*`' intrinsics invalidate the mbarrier | |
| object at the address specified by `addr`. | |
| ##### Semantics: | |
| The `.shared` variant explicitly uses shared memory address space for | |
| the `addr` operand. If the `addr` does not fall within the | |
| shared::cta space, then the behavior of this intrinsic is undefined. | |
| It is expected that `addr` was previously initialized using | |
| `mbarrier.init`; otherwise, the behavior is undefined. | |
| #### '`llvm.nvvm.mbarrier.expect.tx`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.mbarrier.expect.tx.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.expect.tx.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.expect.tx.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.expect.tx.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.mbarrier.expect.tx.*`' intrinsics increase the transaction | |
| count of the mbarrier object at `%addr` by `%tx_count`. The `%tx_count` | |
| is a 32-bit unsigned integer value. | |
| ##### Semantics: | |
| The `.space.{cta/cluster}` indicates the address space where the mbarrier | |
| object resides. | |
| The `.scope.{cta/cluster}` denotes the set of threads that can directly | |
| observe the synchronizing effect of the mbarrier operation. When scope is | |
| "cta", all threads executing in the same CTA (as the current thread) can | |
| directly observe the effect of the `expect.tx` operation. Similarly, | |
| when scope is "cluster", all threads executing in the same Cluster | |
| (as the current thread) can directly observe the effect of the operation. | |
| If the `addr` does not fall within shared::cta or shared::cluster space, | |
| then the behavior of this intrinsic is undefined. This intrinsic has | |
| `relaxed` semantics and hence does not provide any memory ordering | |
| or visibility guarantees. | |
| #### '`llvm.nvvm.mbarrier.complete.tx`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.mbarrier.complete.tx.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.complete.tx.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.complete.tx.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.complete.tx.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.mbarrier.complete.tx.*`' intrinsics decrease the transaction | |
| count of the mbarrier object at `%addr` by `%tx_count`. The `%tx_count` | |
| is a 32-bit unsigned integer value. As a result of this decrement, | |
| the mbarrier can potentially complete its current phase and transition | |
| to the next phase. | |
| ##### Semantics: | |
| The semantics of these intrinsics are identical to those of the | |
| `llvm.nvvm.mbarrier.expect.tx.*` intrinsics described above. | |
| #### '`llvm.nvvm.mbarrier.arrive`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i64 @llvm.nvvm.mbarrier.arrive.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.relaxed.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.relaxed.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.relaxed.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.relaxed.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.mbarrier.arrive.*` intrinsics signal the arrival of the | |
| executing thread or completion of an asynchronous instruction associated with | |
| an arrive operation on the mbarrier object at `%addr`. This operation | |
| decrements the pending arrival count by `%count`, a 32-bit unsigned integer, | |
| potentially completing the current phase and triggering a transition to the | |
| next phase. | |
| ##### Semantics: | |
| The `.space.{cta/cluster}` indicates the address space where the mbarrier | |
| object resides. When the mbarrier is in shared::cta space, the intrinsics | |
| return an opaque 64-bit value capturing the phase of the mbarrier object | |
| \_prior\_ to this arrive operation. This value can be used with a try_wait | |
| or test_wait operation to check for the completion of the mbarrier. | |
| The `.scope.{cta/cluster}` denotes the set of threads that can directly | |
| observe the synchronizing effect of the mbarrier operation. When scope is | |
| "cta", all threads executing in the same CTA (as the current thread) can | |
| directly observe the effect of the `arrive` operation. Similarly, | |
| when scope is "cluster", all threads executing in the same Cluster | |
| (as the current thread) can directly observe the effect of the operation. | |
| If the `addr` does not fall within shared::cta or shared::cluster space, | |
| then the behavior of this intrinsic is undefined. | |
| These intrinsics have `release` semantics by default. The release semantics | |
| ensure ordering of operations that occur in program order \_before\_ this arrive | |
| instruction, making their effects visible to subsequent operations in other | |
| threads of the CTA (or cluster, depending on scope). Threads performing | |
| corresponding acquire operations (such as mbarrier.test.wait) synchronize | |
| with this release. The `relaxed` variants of these intrinsics do not | |
| provide any memory ordering or visibility guarantees. | |
| #### '`llvm.nvvm.mbarrier.arrive.expect.tx`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i64 @llvm.nvvm.mbarrier.arrive.expect.tx.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.expect.tx.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.expect.tx.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.expect.tx.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.expect.tx.relaxed.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.expect.tx.relaxed.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.expect.tx.relaxed.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.expect.tx.relaxed.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.mbarrier.arrive.expect.tx.*` intrinsics are similar to | |
| the `@llvm.nvvm.mbarrier.arrive` intrinsics except that they also | |
| perform an `expect-tx` operation \_prior\_ to the `arrive` operation. | |
| The `%tx_count` specifies the transaction count for the `expect-tx` | |
| operation and the count for the `arrive` operation is assumed to be 1. | |
| ##### Semantics: | |
| The semantics of these intrinsics are identical to those of the | |
| `llvm.nvvm.mbarrier.arrive.*` intrinsics described above. | |
| #### '`llvm.nvvm.mbarrier.arrive.drop`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.relaxed.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.relaxed.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.relaxed.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.relaxed.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %count) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.mbarrier.arrive.drop.*` intrinsics decrement the | |
| expected arrival count of the mbarrier object at `%addr` by | |
| `%count` and then perform an `arrive` operation with `%count`. | |
| The `%count` is a 32-bit integer. | |
| ##### Semantics: | |
| The semantics of these intrinsics are identical to those of the | |
| `llvm.nvvm.mbarrier.arrive.*` intrinsics described above. | |
| #### '`llvm.nvvm.mbarrier.arrive.drop.expect.tx`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.expect.tx.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.expect.tx.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.expect.tx.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.expect.tx.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.expect.tx.relaxed.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare i64 @llvm.nvvm.mbarrier.arrive.drop.expect.tx.relaxed.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.expect.tx.relaxed.scope.cta.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| declare void @llvm.nvvm.mbarrier.arrive.drop.expect.tx.relaxed.scope.cluster.space.cluster(ptr addrspace(7) %addr, i32 %tx_count) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.mbarrier.arrive.drop.expect.tx.*` intrinsics perform | |
| the below operations on the mbarrier located at `%addr`. | |
| - Perform an `expect-tx` operation i.e. increase the transaction count | |
| of the mbarrier by `%tx_count`, a 32-bit unsigned integer value. | |
| - Decrement the expected arrival count of the mbarrier by 1. | |
| - Perform an `arrive` operation on the mbarrier with a value of 1. | |
| ##### Semantics: | |
| The semantics of these intrinsics are identical to those of the | |
| `llvm.nvvm.mbarrier.arrive.*` intrinsics described above. | |
| #### '`llvm.nvvm.mbarrier.test.wait`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.scope.cta.space.cta(ptr addrspace(3) %addr, i64 %state) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.scope.cluster.space.cta(ptr addrspace(3) %addr, i64 %state) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.parity.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %phase) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.parity.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %phase) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.relaxed.scope.cta.space.cta(ptr addrspace(3) %addr, i64 %state) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.relaxed.scope.cluster.space.cta(ptr addrspace(3) %addr, i64 %state) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.parity.relaxed.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %phase) | |
| declare i1 @llvm.nvvm.mbarrier.test.wait.parity.relaxed.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %phase) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.mbarrier.test.wait.*` intrinsics test for the completion | |
| of the current or the immediately preceding phase of an mbarrier object at | |
| `%addr`. The test for completion can be done with either the `state` or | |
| the `phase-parity` of the mbarrier object. | |
| - When done through the `i64 %state` operand, the state must be | |
| returned by an `llvm.nvvm.mbarrier.arrive.*` on the \_same\_ | |
| mbarrier object. | |
| - The `.parity` variant of these intrinsics test for completion | |
| of the phase indicated by the operand `i32 %phase`, which is | |
| the integer parity of either the current phase or the immediately | |
| preceding phase of the mbarrier object. An even phase has integer | |
| parity 0 and an odd phase has integer parity of 1. So the valid | |
| values for phase-parity are 0 and 1. | |
| ##### Semantics: | |
| The `.scope.{cta/cluster}` denotes the set of threads that the | |
| test_wait operation can directly synchronize with. | |
| If the `addr` does not fall within shared::cta space, then the | |
| the behavior of this intrinsic is undefined. | |
| These intrinsics have `acquire` semantics by default. This acquire | |
| pattern establishes memory ordering for operations occurring in program | |
| order after this `test_wait` instruction by making operations from | |
| other threads in the CTA (or cluster, depending on scope) visible to | |
| subsequent operations in the current thread. When this wait completes, | |
| it synchronizes with the corresponding release pattern from the | |
| `mbarrier.arrive` operation. The `relaxed` variants of these intrinsics | |
| do not provide any memory ordering or visibility guarantees. | |
| This `test.wait` intrinsic is non-blocking and immediately returns | |
| the completion status without suspending the executing thread. | |
| The boolean return value indicates: | |
| - True: The immediately preceding phase has completed | |
| - False: The current phase is still incomplete | |
| When this wait returns true, the following ordering guarantees hold: | |
| - All memory accesses (except async operations) requested prior to | |
| `mbarrier.arrive` having release semantics by participating | |
| threads of a CTA (or cluster, depending on scope) are visible to | |
| the executing thread. | |
| - All `cp.async` operations requested prior to `cp.async.mbarrier.arrive` | |
| by participating threads of a CTA are visible to the executing thread. | |
| - All `cp.async.bulk` operations using the same mbarrier object requested | |
| prior to `mbarrier.arrive` having release semantics by participating CTA | |
| threads are visible to the executing thread. | |
| - Memory accesses requested after this wait are not visible to memory | |
| accesses performed prior to `mbarrier.arrive` by other participating | |
| threads. | |
| - No ordering guarantee exists for memory accesses by the same thread | |
| between an `mbarrier.arrive` and this wait. | |
| #### '`llvm.nvvm.mbarrier.try.wait`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i1 @llvm.nvvm.mbarrier.try.wait{.relaxed}.scope.cta.space.cta(ptr addrspace(3) %addr, i64 %state) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait{.relaxed}.scope.cluster.space.cta(ptr addrspace(3) %addr, i64 %state) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait.parity{.relaxed}.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %phase) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait.parity{.relaxed}.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %phase) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait.tl{.relaxed}.scope.cta.space.cta(ptr addrspace(3) %addr, i64 %state, i32 %timelimit) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait.tl{.relaxed}.scope.cluster.space.cta(ptr addrspace(3) %addr, i64 %state, i32 %timelimit) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait.parity.tl{.relaxed}.scope.cta.space.cta(ptr addrspace(3) %addr, i32 %phase, i32 %timelimit) | |
| declare i1 @llvm.nvvm.mbarrier.try.wait.parity.tl{.relaxed}.scope.cluster.space.cta(ptr addrspace(3) %addr, i32 %phase, i32 %timelimit) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.mbarrier.try.wait.*` intrinsics test for the completion of | |
| the current or immediately preceding phase of an mbarrier object at `%addr`. | |
| Unlike the `test.wait` intrinsics, which perform a non-blocking test, these | |
| intrinsics may block the executing thread until the specified phase completes | |
| or a system-dependent time limit expires. Suspended threads resume execution | |
| when the phase completes or the time limit elapses. This time limit is | |
| configurable through the `.tl` variants of these intrinsics, where the | |
| `%timelimit` operand (an unsigned integer) specifies the limit in | |
| nanoseconds. Other semantics are identical to those of the `test.wait` | |
| intrinsics described above. | |
| ### Electing a thread | |
| #### '`llvm.nvvm.elect.sync`' | |
| ##### Syntax: | |
| ```llvm | |
| declare {i32, i1} @llvm.nvvm.elect.sync(i32 %membermask) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.elect.sync`' intrinsic generates the `elect.sync` | |
| PTX instruction, which elects one predicated active leader thread from | |
| a set of threads specified by `membermask`. The behavior is undefined | |
| if the executing thread is not in `membermask`. The laneid of the | |
| elected thread is captured in the i32 return value. The i1 return | |
| value is set to `True` for the leader thread and `False` for all | |
| the other threads. Election of a leader thread happens deterministically, | |
| i.e. the same leader thread is elected for the same `membermask` | |
| every time. For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-elect-sync). | |
| ### Membar/Fences | |
| #### '`llvm.nvvm.fence.acquire/release.sync_restrict.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.fence.acquire.sync_restrict.space.cluster.scope.cluster() | |
| declare void @llvm.nvvm.fence.release.sync_restrict.space.cta.scope.cluster() | |
| ``` | |
| ##### Overview: | |
| The `nvvm.fence.{semantics}.sync_restrict.*` restrict the class of memory | |
| operations for which the fence instruction provides the memory ordering | |
| guarantees. When `.sync_restrict` is restricted to `shared_cta`, then memory | |
| semantics must be `release` and the effect of the fence operation only applies | |
| to operations performed on objects in `shared_cta` space. Likewise, when | |
| `sync_restrict` is restricted to `shared_cluster`, then memory semantics must be | |
| `acquire` and the effect of the fence operation only applies to operations | |
| performed on objects in `shared_cluster` memory space. The scope for both | |
| operations is `cluster`. For more details, please refer the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-membar). | |
| #### '`llvm.nvvm.fence.mbarrier_init.release.cluster`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.fence.mbarrier_init.release.cluster() | |
| ``` | |
| ##### Overview: | |
| `nvvm.fence.mbarrier_init.release.cluster` intrinsic restrict the class of | |
| memory operations for which the fence instruction provides the memory ordering | |
| guarantees. The `mbarrier_init` modifiers restricts the synchronizing effect to | |
| the prior `mbarrier_init` operation executed by the same thread on mbarrier | |
| objects in `shared_cta` memory space. For more details, please refer the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-membar) | |
| #### '`llvm.nvvm.fence.proxy.async_generic.acquire/release.sync_restrict`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.fence.proxy.async_generic.acquire.sync_restrict.space.cluster.scope.cluster() | |
| declare void @llvm.nvvm.fence.proxy.async_generic.release.sync_restrict.space.cta.scope.cluster() | |
| ``` | |
| ##### Overview: | |
| `nvvm.fence.proxy.async_generic.{semantics}.sync_restrict` are used to establish | |
| ordering between a prior memory access performed via the [async proxy](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#proxies) | |
| and a subsequent memory access performed via the generic proxy. | |
| `nvvm.fence.proxy.async_generic.release.sync_restrict` can form a release | |
| sequence that synchronizes with an acquire sequence that contains the | |
| `nvvm.fence.proxy.async_generic.acquire.sync_restrict` proxy fence. When | |
| `.sync_restrict` is restricted to `shared_cta`, then memory semantics must | |
| be `release` and the effect of the fence operation only applies to operations | |
| performed on objects in `shared_cta` space. Likewise, when `sync_restrict` is | |
| restricted to `shared_cluster`, then memory semantics must be `acquire` and the | |
| effect of the fence operation only applies to operations performed on objects in | |
| `shared_cluster` memory space. The scope for both operations is `cluster`. | |
| For more details, please refer the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-membar) | |
| #### '`llvm.nvvm.fence.proxy.<proxykind>`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.fence.proxy.alias() | |
| declare void @llvm.nvvm.fence.proxy.async() | |
| declare void @llvm.nvvm.fence.proxy.async.global() | |
| declare void @llvm.nvvm.fence.proxy.async.shared_cluster() | |
| declare void @llvm.nvvm.fence.proxy.async.shared_cta() | |
| ``` | |
| ##### Overview: | |
| `nvvm.fence.proxy.{proxykind}` intrinsics represent a fence with bi-directional | |
| proxy ordering that is established between the memory accesses done between the | |
| [generic proxy](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#proxies) | |
| and the proxy specified by `proxykind`. A `bi-directional proxy` ordering between | |
| two proxykinds establishes two `uni-directional` proxy orderings: one from the | |
| first proxykind to the second proxykind and the other from the second proxykind | |
| to the first proxykind. | |
| `alias` proxykind refers to memory accesses performed using virtually aliased | |
| addresses to the same memory location | |
| `async` proxykind specifies that the memory ordering is established between the | |
| `async proxy` and the `generic proxy`. The memory ordering is limited only to | |
| operations performed on objects in the state space specified (`generic`, `global`, | |
| `shared_cluster`, `shared_cta`). If no state space is specified, then the memory | |
| ordering applies on all state spaces. For more details, please refer the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#parallel-synchronization-and-communication-instructions-membar) | |
| #### '`llvm.nvvm.fence.proxy.tensormap_generic.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.release.cta() | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.release.cluster() | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.release.gpu() | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.release.sys() | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.acquire.cta(ptr %addr, i32 %size) | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.acquire.cluster(ptr %addr, i32 %size) | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.acquire.gpu(ptr %addr, i32 %size) | |
| declare void @llvm.nvvm.fence.proxy.tensormap_generic.acquire.sys(ptr %addr, i32 %size) | |
| ``` | |
| ##### Overview: | |
| The `@llvm.nvvm.fence.proxy.tensormap_generic.*` is a uni-directional fence | |
| used to establish ordering between a prior memory access performed via the | |
| generic [proxy](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#proxies) | |
| and a subsequent memory access performed via the tensormap proxy. | |
| `nvvm.fence.proxy.tensormap_generic.release` can form a release sequence that | |
| synchronizes with an acquire sequence that contains the | |
| `nvvm.fence.proxy.tensormap_generic.acquire` proxy fence. The following table | |
| describes the mapping between LLVM Intrinsic and the PTX instruction: | |
| | NVVM Intrinsic | PTX Instruction | | |
| | ---------------------------------------------------- | ------------------------------------------------------- | | |
| | `@llvm.nvvm.fence.proxy.tensormap_generic.release.*` | `fence.proxy.tensormap::generic.release.*` | | |
| | `@llvm.nvvm.fence.proxy.tensormap_generic.acquire.*` | `fence.proxy.tensormap::generic.acquire.* [addr], size` | | |
| The address operand `addr` and the operand `size` together specify the | |
| memory range `[addr, addr+size)` on which the ordering guarantees on the | |
| memory accesses across the proxies is to be provided. The only supported value | |
| for the `size` operand is `128` and must be an immediate. Generic Addressing | |
| is used unconditionally, and the address specified by the operand addr must fall | |
| within the `.global` state space. Otherwise, the behavior is undefined. For | |
| more information, see [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#parallel-synchronization-and-communication-instructions-membar). | |
| ### Address Space Intrinsics | |
| #### '`llvm.nvvm.isspacep.*`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i1 @llvm.nvvm.isspacep.const(ptr %p) | |
| declare i1 @llvm.nvvm.isspacep.global(ptr %p) | |
| declare i1 @llvm.nvvm.isspacep.local(ptr %p) | |
| declare i1 @llvm.nvvm.isspacep.shared(ptr %p) | |
| declare i1 @llvm.nvvm.isspacep.shared.cluster(ptr %p) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.isspacep.*`' intrinsics determine whether the provided generic | |
| pointer references memory which falls within a particular address space. | |
| ##### Semantics: | |
| If the given pointer in the generic address space refers to memory which falls | |
| within the state space of the intrinsic (and therefore could be safely address | |
| space casted to this space), 1 is returned, otherwise 0 is returned. | |
| #### '`llvm.nvvm.mapa.*`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare ptr @llvm.nvvm.mapa(ptr %p, i32 %rank) | |
| declare ptr addrspace(7) @llvm.nvvm.mapa.shared.cluster(ptr addrspace(3) %p, i32 %rank) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.mapa.*`' intrinsics map a shared memory pointer `p` of | |
| another CTA with `%rank` to the current CTA. The `llvm.nvvm.mapa` form | |
| expects a generic pointer to shared memory and returns a generic pointer to | |
| shared cluster memory. The `llvm.nvvm.mapa.shared.cluster` form expects a | |
| pointer to shared memory and returns a pointer to shared cluster memory. They | |
| corresponds directly to the `mapa` and `mapa.shared.cluster` PTX | |
| instructions. | |
| ##### Semantics: | |
| If the given pointer in the generic address space refers to memory which falls | |
| within the state space of the intrinsic (and therefore could be safely address | |
| space casted to this space), 1 is returned, otherwise 0 is returned. | |
| ### Structured Sparsity Intrinsics | |
| The `llvm.nvvm.spcompress.*` and `llvm.nvvm.spdecompress.*` intrinsics model | |
| the PTX `spcompress` and `spdecompress` instructions. They require PTX ISA 9.4 | |
| and `sm_107a`. | |
| The intrinsic types representing `mdata`, `cdata`, and `data` are overloaded. | |
| The packed metadata remains a bundle of 32-bit registers: one register is | |
| represented by `i32`, and `N` registers are represented by `<N x i32>`. The | |
| `cdata` and `data` vectors instead use one `i8` or `i16` lane per logical | |
| element. Lowering packs these lanes into 32-bit PTX register operands and pads | |
| an incomplete final register when necessary. The actual intrinsic names | |
| include the corresponding LLVM overload suffixes. | |
| The immediate qualifiers have the following encodings: | |
| | Argument | Values | PTX qualifiers | | |
| | ----------- | --------------------- | ------------------------------- | | |
| | `%idx_size` | `2`, `4` | `.b2`, `.b4` | | |
| | `%num_tgt` | `2`, `4`, `8`, `16` | target group size in `.sp::X:Y` | | |
| The PTX element-size qualifier (`.b8` or `.b16`) is inferred from the `i8` or | |
| `i16` element type. The repeat-factor qualifier is inferred from the bundle | |
| sizes. `%num_tgt` is `4` for `spcompress`. | |
| #### '`llvm.nvvm.spcompress`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare {MDataTy, CDataTy} @llvm.nvvm.spcompress( | |
| DataTy %data, i32 %spdesc, i32 immarg %idx_size, | |
| i32 immarg %num_tgt) | |
| ``` | |
| ##### Overview: | |
| This intrinsic compresses the dense vector `%data` using 2:4 structured | |
| sparsity. `%num_tgt` specifies the target group size and must be `4`; the | |
| source-to-target ratio is inferred from the `CDataTy` and `DataTy` vector | |
| lengths. In `.sp::X:Y`, `Y` is `%num_tgt` and | |
| `X = Y * num_elements(CDataTy) / num_elements(DataTy)`; the division must be | |
| exact. It returns the selected element indices as `mdata` and the selected | |
| elements as `cdata`. The number of 32-bit registers in each bundle is: | |
| | Bundle | Register count | | |
| | ------- | ----------------------------------------- | | |
| | `data` | `2 * num` | | |
| | `cdata` | `num` | | |
| | `mdata` | `ceil(num * %idx_size / elem_size)` | | |
| Here, `elem_size` is the scalar bit width of `DataTy` and `CDataTy`. Both types | |
| must use the same `i8` or `i16` element type. `num` is half the number of | |
| registers in `DataTy` and determines the PTX repeat-factor qualifier (`.x1`, | |
| `.x2`, ..., `.x64`). | |
| The combined `mdata`, `cdata`, and `data` bundle size must not exceed 253 | |
| registers. | |
| The `%spdesc` operand specifies the selection operation and the element data | |
| type: | |
| | Bits | Description | Values | | |
| | ---- | ------------------- | ------------------------------------------------------------------------- | | |
| | 0-1 | Selection operation | `0`: max, `1`: maxabs, `2`: min, `3`: minabs | | |
| | 2-4 | Element data type | `0`: f16/u8, `1`: bf16/s8, `2`: e5m2, `3`: e4m3, `4`: e3m2, `5`: e2m3 | | |
| | 5-31 | Reserved | `0` | | |
| The element data type selected by `%spdesc` must be consistent with the scalar | |
| element type of `DataTy` and `CDataTy`. When the metadata occupies less than 32 | |
| bits, it is zero-extended to fill its `i32` register. | |
| The operation treats negative zero as less than positive zero. NaN elements | |
| are always selected; when multiple selections satisfy the same comparison, | |
| the selected indices are implementation-specific. | |
| For more information, see the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-spcompress). | |
| #### '`llvm.nvvm.spdecompress`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare DataTy @llvm.nvvm.spdecompress( | |
| MDataTy %mdata, CDataTy %cdata, i32 immarg %idx_size, | |
| i32 immarg %num_tgt) | |
| ``` | |
| ##### Overview: | |
| This intrinsic decompresses the structured sparse vector `%cdata` into a dense | |
| `data` vector. `%num_tgt` specifies `Y`, the number of dense elements in each | |
| target group. The source group size `X` in `.sp::X:Y` is calculated from the | |
| vector lengths as: | |
| `X = Y * num_elements(CDataTy) / num_elements(DataTy)` | |
| The division must be exact. For example, two compressed lanes for every four | |
| result lanes with `%num_tgt = 4` select `.sp::2:4`. Dense positions not selected | |
| by the metadata are set to zero. | |
| Let `Y` be `%num_tgt`. The repeat factor `num` and source group size `X` are: | |
| - `num = num_elements(DataTy) / Y`. | |
| - `X = num_elements(CDataTy) / num`. | |
| The number of 32-bit PTX registers in each bundle is: | |
| | Bundle | Register count | | |
| | ------- | ------------------------------------------------- | | |
| | `mdata` | `ceil(X * %idx_size * num / 32)` | | |
| | `cdata` | `ceil(X * elem_size * num / 32)` | | |
| | `data` | `ceil(Y * elem_size * num / 32)` | | |
| Here, `elem_size` is the common `i8` or `i16` scalar bit width of `CDataTy` and | |
| `DataTy`. `num` determines the PTX repeat-factor qualifier (`.x1`, `.x2`, ..., | |
| `.x64`). Padding needed to fill the final compressed-data register is added | |
| internally during lowering and is not represented in `CDataTy`. | |
| The following conditions must hold: | |
| - `X:Y` is one of `1:2`, `1:4`, `1:8`, `1:16`, `2:4`, `2:8`, `2:16`, | |
| `4:8`, or `4:16`; consequently, `X < Y`. | |
| - `X * elem_size <= 32`. | |
| - `%idx_size` is `2` only when `Y <= 4`. | |
| - `32 <= Y * elem_size * num <= 4096`. | |
| - The combined `mdata`, `cdata`, and `data` bundle size does not exceed 253 | |
| registers. | |
| The behavior is implementation-specific if a metadata index does not identify | |
| a position in its target group. | |
| For more information, see the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-spdecompress). | |
| ### Narrow Floating-Point Conversion intrinsics | |
| These intrinsics perform conversions involving narrow floating-point formats. | |
| The following table describes the rounding modes used across these intrinsics: | |
| (narrow-fp-rounding-modes)= | |
| ```{list-table} Narrow Floating-Point Conversion Rounding Modes | |
| :widths: 30 60 | |
| :header-rows: 1 | |
| * - Rounding Mode | |
| - Description | |
| * - `rn` (default) | |
| - Round to nearest, with ties to even | |
| * - `rz` | |
| - Round towards zero | |
| * - `rp` | |
| - Round towards positive infinity | |
| * - `rs` | |
| - Stochastic rounding which is achieved through the use of the supplied | |
| random bits (`%rnd_bits`). The result s rounded in the direction towards | |
| zero or away from zero based on the carry out of the integer addition of | |
| the of mantissa from the input. | |
| ``` | |
| (scale-factor)= | |
| Some conversions involve a scale factor of type `ue8m0`. For | |
| `scale.n2.ue8m0`, the operand is a packed 16-bit integer containing two | |
| `ue8m0` scale values, one for each input. For `scale.n1.ue8m0`, a single `ue8m0` | |
| scale factor is applied to both inputs. For down conversion, inputs are divided | |
| by `scale_factor` and then the conversion is performed. For up-conversion, | |
| inputs are converted to destination type and then multiplied by `scale_factor`. | |
| #### `fp8` Conversion Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i16 @llvm.nvvm.ff.to{.e4m3x2, .e5m2x2}.rn{.relu}(float %a, float %b) | |
| declare i16 @llvm.nvvm.ff.to.ue8m0x2{.rz, .rp}{.satfinite}(float %a, float %b) | |
| declare i16 @llvm.nvvm.ff.to.ue5m3x2{.rn, .rz, .rp}{.satfinite}(float %a, float %b) | |
| declare i16 @llvm.nvvm.ff.to.ue5m3x2{.rn, .rz}{.satfinite}.scale.n1.ue8m0(float %a, float %b, i16 %scale_factor) | |
| declare i16 @llvm.f16x2.to{.e4m3x2, .e5m2x2}.rn{.relu}(<2 x half> %a) | |
| declare i16 @llvm.nvvm.f16x2.to.ue5m3x2{.rn, .rz, .rp}{.satfinite}(<2 x half> %a) | |
| declare i16 @llvm.nvvm.f16x2.to.ue5m3x2{.rn, .rz}{.satfinite}.scale.n1.ue8m0(<2 x half> %a, i16 %scale_factor) | |
| declare i16 @llvm.bf16x2.to{.e4m3x2, .e5m2x2}.rn{.relu}.satfinite(<2 x bfloat> %a) | |
| declare i16 @llvm.bf16x2.to.ue8m0x2{.rz, .rp}{.satfinite}(<2 x bfloat> %a) | |
| declare i16 @llvm.nvvm.bf16x2.to.ue5m3x2{.rn, .rz, .rp}{.satfinite}(<2 x bfloat> %a) | |
| declare i16 @llvm.nvvm.bf16x2.to.ue5m3x2{.rn, .rz}{.satfinite}.scale.n1.ue8m0(<2 x bfloat> %a, i16 %scale_factor) | |
| declare <2 x half> @llvm.nvvm{.e4m3x2, .e5m2x2}.to.f16x2.rn{.relu}(i16 %a) | |
| declare <2 x half> @llvm.nvvm.ue5m3x2.to.f16x2.rn(i16 %a) | |
| declare <2 x bfloat> @llvm.nvvm{.e4m3x2, .e5m2x2}.to.bf16x2.rn{.relu}{.satfinite}.scale.n2.ue8m0(i16 %a, i16 %scale_factor) | |
| declare <2 x bfloat> @llvm.nvvm.ue8m0x2.to.bf16x2(i16 %a) | |
| declare <2 x bfloat> @llvm.nvvm.ue5m3x2.to.bf16x2.rn{.satfinite}(i16 %a) | |
| declare <2 x bfloat> @llvm.nvvm.ue5m3x2.to.bf16x2.rn{.satfinite}.scale.n2.ue8m0(i16 %a, i16 %scale_factor) | |
| declare <4 x i8> @llvm.nvvm.f32x4.to{.e4m3x4, .e5m2x4}.rs{.relu}.satfinite(<4 x f32> %a, i32 %rnd_bits) | |
| ``` | |
| ##### Overview: | |
| These intrinsics perform conversions involving the `e4m3`, `e5m2`, `ue8m0`, | |
| and `ue5m3` narrow floating-point formats. In case of two inputs, the value | |
| converted from input `%a` is stored in the upper 8-bits of the result, and | |
| the value converted from input `%b` is stored in the lower 8-bits of the | |
| result. | |
| For rounding modes, see {ref}`narrow-fp-rounding-modes`. | |
| The `relu` modifier clamps negative results to 0. | |
| When `satfinite` is specified, if the absolute value of input (ignoring sign) | |
| is greater than `MAX_NORM` of the specified destination format, then the | |
| result is sign-preserved `MAX_NORM` of the destination format and a positive | |
| `MAX_NORM` in `.ue8m0x2`/`.ue5m3x2` for which the destination sign is not | |
| supported. Also, if the input value is `NaN`, then the result is `NaN` in | |
| the specified destination format. The `satfinite` modifier is assumed to be | |
| present for conversions involving `e4m3` and `e5m2` types as the | |
| destination. | |
| For scale factor, see {ref}`scale-factor <scale-factor>`. | |
| For more information, see [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cvt). | |
| #### `s2f6` Conversion Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i16 @llvm.nvvm.ff.to.s2f6x2.rn{.relu}.satfinite.scale.n2.ue8m0(float %a, float %b, i16 %scale_factor) | |
| declare i16 @llvm.nvvm.bf16x2.to.s2f6x2.rn{.relu}.satfinite.scale.n2.ue8m0(<2 x bfloat> %a, i16 %scale_factor) | |
| declare <2 x bfloat> @llvm.nvvm.s2f6x2.to.bf16x2.rn{.relu}{.satfinite}.scale.n2.ue8m0(i16 %a, i16 %scale_factor) | |
| ``` | |
| ##### Overview: | |
| These intrinsics perform conversions involving the `s2f6` narrow | |
| floating-point format. In case of two inputs, the value converted from input | |
| `%a` is stored in the upper 8-bits of the result, and the value converted | |
| from input `%b` is stored in the lower 8-bits of the result. | |
| For rounding modes, see {ref}`narrow-fp-rounding-modes`. | |
| The `relu` modifier clamps negative results to 0. | |
| When `satfinite` is specified, if the absolute value of input (ignoring sign) | |
| is greater than `MAX_NORM` of the specified destination format, then the | |
| result is sign-preserved `MAX_NORM` of the destination format. Also, if the | |
| input is `NaN`, then the result is the positive `MAX_NORM` of the | |
| destination format. | |
| For scale factor, see {ref}`scale-factor <scale-factor>`. | |
| For more information, see [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cvt). | |
| #### `fp6` Conversion Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i16 @llvm.nvvm.ff.to{.e2m3x2, .e3m2x2}.rn{.relu}.satfinite(float %a, float %b) | |
| declare i16 @llvm.nvvm.f16x2.to{.e2m3x2, .e3m2x2}.rn{.relu}.satfinite(<2 x half> %a) | |
| declare i16 @llvm.nvvm.bf16x2.to{.e2m3x2, .e3m2x2}.rn{.relu}.satfinite(<2 x bfloat> %a) | |
| declare <2 x half> @llvm.nvvm{.e2m3x2, .e3m2x2}.to.f16x2.rn{.relu}(i16 %a) | |
| declare <2 x bfloat> @llvm.nvvm{.e2m3x2, .e3m2x2}.to.bf16x2.rn{.relu}{.satfinite}.scale.n2.ue8m0(i16 %a, i16 %scale_factor) | |
| declare <4 x i8> @llvm.nvvm.f32x4.to{.e2m3x4, .e3m2x4}.rs{.relu}.satfinite(<4 x f32> %a, i32 %rnd_bits) | |
| ``` | |
| ##### Overview: | |
| These intrinsics perform conversions involving the `e2m3` and `e3m2` narrow | |
| floating-point formats. In case of two inputs, the value converted from input | |
| `%a` is stored in the upper 8-bits of the result, and the value converted | |
| from input `%b` is stored in the lower 8-bits of the result with 2 MSBs | |
| padded with 0s in both cases. | |
| For rounding modes, see {ref}`narrow-fp-rounding-modes`. | |
| The `relu` modifier clamps negative results to 0. | |
| When `satfinite` is specified, if the absolute value of input (ignoring sign) | |
| is greater than `MAX_NORM` of the specified destination format, then the | |
| result is sign-preserved `MAX_NORM` of the destination format. Also, if the | |
| input is `NaN`, then the result is the positive `MAX_NORM` of the | |
| destination format. | |
| For scale factor, see {ref}`scale-factor <scale-factor>`. | |
| For more information, see [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cvt). | |
| #### `fp4` Conversion Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i16 @llvm.nvvm.ff.to.e2m1x2.rn{.relu}.satfinite(float %a, float %b) | |
| declare i16 @llvm.nvvm.f16x2.to.e2m1x2.rn{.relu}.satfinite(<2 x half> %a) | |
| declare i16 @llvm.nvvm.bf16x2.to.e2m1x2.rn{.relu}.satfinite(<2 x bfloat> %a) | |
| declare <2 x half> @llvm.nvvm.e2m1x2.to.f16x2.rn{.relu}(i16 %a) | |
| declare <2 x bfloat> @llvm.nvvm.e2m1x2.to.bf16x2.rn{.relu}{.satfinite}.scale.n2.ue8m0(i16 %a, i16 %scale_factor) | |
| declare i16 @llvm.nvvm.f32x4.to.e2m1x4.rs{.relu}.satfinite(<4 x f32> %a, i32 %rnd_bits) | |
| ``` | |
| ##### Overview: | |
| These intrinsics perform conversions involving the `e2m1` narrow | |
| floating-point format. For conversions involving `e2m1x2`, the packed | |
| `e2m1x2` value is stored in the lower byte of the `i16` argument or result. | |
| In case of two inputs, the value converted from input | |
| `%a` is stored in the upper 4-bits of the result, and the value converted | |
| from input `%b` is stored in the lower 4-bits of the result. | |
| For rounding modes, see {ref}`narrow-fp-rounding-modes`. | |
| The `relu` modifier clamps negative results to 0. | |
| When `satfinite` is specified, if the absolute value of input (ignoring sign) | |
| is greater than `MAX_NORM` of the specified destination format, then the | |
| result is sign-preserved `MAX_NORM` of the destination format. Also, if the | |
| input is `NaN`, then the result is the positive `MAX_NORM` of the | |
| destination format. | |
| For scale factor, see {ref}`scale-factor <scale-factor>`. | |
| For more information, see [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cvt). | |
| ### Arithmetic Intrinsics | |
| Some of these intrinsics take the rounding mode as an `i32` immediate operand | |
| instead of encoding it in the intrinsic name. The accepted values match the | |
| `llvm::RoundingMode` enumeration and are described in the following table: | |
| (fp-rounding-modes)= | |
| ```{list-table} Floating-Point Rounding Modes | |
| :widths: 15 15 70 | |
| :header-rows: 1 | |
| * - Value | |
| - Rounding Mode | |
| - Description | |
| * - 0 | |
| - `rz` | |
| - Round towards zero | |
| * - 1 | |
| - `rn` | |
| - Round to nearest, with ties to even | |
| * - 2 | |
| - `rp` | |
| - Round towards positive infinity | |
| * - 3 | |
| - `rm` | |
| - Round towards negative infinity | |
| ``` | |
| #### '`llvm.nvvm.fabs.*`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare float @llvm.nvvm.fabs.f32(float %a) | |
| declare double @llvm.nvvm.fabs.f64(double %a) | |
| declare half @llvm.nvvm.fabs.f16(half %a) | |
| declare <2 x half> @llvm.nvvm.fabs.v2f16(<2 x half> %a) | |
| declare bfloat @llvm.nvvm.fabs.bf16(bfloat %a) | |
| declare <2 x bfloat> @llvm.nvvm.fabs.v2bf16(<2 x bfloat> %a) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fabs.*`' intrinsics return the absolute value of the operand. | |
| ##### Semantics: | |
| Unlike, '`llvm.fabs.*`', these intrinsics do not perfectly preserve NaN | |
| values. Instead, a NaN input yields an unspecified NaN output. | |
| #### '`llvm.nvvm.fabs.ftz.*`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare float @llvm.nvvm.fabs.ftz.f32(float %a) | |
| declare half @llvm.nvvm.fabs.ftz.f16(half %a) | |
| declare <2 x half> @llvm.nvvm.fabs.ftz.v2f16(<2 x half> %a) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fabs.ftz.*`' intrinsics return the absolute value of the | |
| operand, flushing subnormals to sign preserving zero. | |
| ##### Semantics: | |
| Before the absolute value is taken, the input is flushed to sign preserving | |
| zero if it is a subnormal. In addition, unlike '`llvm.fabs.*`', a NaN input | |
| yields an unspecified NaN output. | |
| #### '`llvm.nvvm.idp2a.[us].[us]`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.idp2a.s.s(i32 %a, i32 %b, i1 immarg %is.hi, i32 %c) | |
| declare i32 @llvm.nvvm.idp2a.s.u(i32 %a, i32 %b, i1 immarg %is.hi, i32 %c) | |
| declare i32 @llvm.nvvm.idp2a.u.s(i32 %a, i32 %b, i1 immarg %is.hi, i32 %c) | |
| declare i32 @llvm.nvvm.idp2a.u.u(i32 %a, i32 %b, i1 immarg %is.hi, i32 %c) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.idp2a.[us].[us]`' intrinsics performs a 2-element vector dot | |
| product followed by addition. They corresponds directly to the `dp2a` PTX | |
| instruction. | |
| ##### Semantics: | |
| The 32-bit value in `%a` is broken into 2 16-bit values which are extended to | |
| 32 bits. For the '`llvm.nvvm.idp2a.u.[us]`' variants zero-extension is used, | |
| while for the '`llvm.nvvm.idp2a.s.[us]`' sign-extension is used. Two bytes are | |
| selected from `%b`, if `%is.hi` is true, the most significant bytes are | |
| selected, otherwise the least significant bytes are selected. These bytes are | |
| then extended to 32-bits. For the '`llvm.nvvm.idp2a.[us].u`' variants | |
| zero-extension is used, while for the '`llvm.nvvm.idp2a.[us].s`' | |
| sign-extension is used. The dot product of these 2-element vectors is added to | |
| `%c` to produce the return. | |
| #### '`llvm.nvvm.idp4a.[us].[us]`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.idp4a.s.s(i32 %a, i32 %b, i32 %c) | |
| declare i32 @llvm.nvvm.idp4a.s.u(i32 %a, i32 %b, i32 %c) | |
| declare i32 @llvm.nvvm.idp4a.u.s(i32 %a, i32 %b, i32 %c) | |
| declare i32 @llvm.nvvm.idp4a.u.u(i32 %a, i32 %b, i32 %c) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.idp4a.[us].[us]`' intrinsics perform a 4-element vector dot | |
| product followed by addition. They corresponds directly to the `dp4a` PTX | |
| instruction. | |
| ##### Semantics: | |
| Each of the 4 bytes in both `%a` and `%b` are extended to 32-bit integers | |
| forming 2 `<4 x i32>`. For `%a`, zero-extension is used in the | |
| '`llvm.nvvm.idp4a.u.[us]`' variants, while sign-extension is used with | |
| '`llvm.nvvm.idp4a.s.[us]`' variants. Similarly, for `%b`, zero-extension is | |
| used in the '`llvm.nvvm.idp4a.[us].u`' variants, while sign-extension is used | |
| with '`llvm.nvvm.idp4a.[us].s`' variants. The dot product of these 4-element | |
| vectors is added to `%c` to produce the return. | |
| #### '`llvm.nvvm.fadd.*`' Intrinsics | |
| ##### Syntax: | |
| This is an overloaded intrinsic of the form: | |
| ```llvm | |
| declare <ty> @llvm.nvvm.fadd{.ftz}{.sat}.<suffix>(<ty> %a, <ty> %b, i32 immarg %flag_fp_rnd_mode) | |
| ``` | |
| where '`<suffix>`' is the mangled suffix of the overloaded type '`<ty>`' and the | |
| '`.ftz`' and '`.sat`' modifiers are optional. The supported variants are: | |
| ```llvm | |
| declare half @llvm.nvvm.fadd{.ftz}{.sat}.f16(half %a, half %b, i32 immarg %flag_fp_rnd_mode) | |
| declare <2 x half> @llvm.nvvm.fadd{.ftz}{.sat}.v2f16(<2 x half> %a, <2 x half> %b, i32 immarg %flag_fp_rnd_mode) | |
| declare bfloat @llvm.nvvm.fadd.bf16(bfloat %a, bfloat %b, i32 immarg %flag_fp_rnd_mode) | |
| declare <2 x bfloat> @llvm.nvvm.fadd.v2bf16(<2 x bfloat> %a, <2 x bfloat> %b, i32 immarg %flag_fp_rnd_mode) | |
| declare float @llvm.nvvm.fadd{.ftz}{.sat}.f32(float %a, float %b, i32 immarg %flag_fp_rnd_mode) | |
| declare <2 x float> @llvm.nvvm.fadd{.ftz}.v2f32(<2 x float> %a, <2 x float> %b, i32 immarg %flag_fp_rnd_mode) | |
| declare double @llvm.nvvm.fadd.f64(double %a, double %b, i32 immarg %flag_fp_rnd_mode) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fadd.*`' intrinsics add `%a` and `%b` using the rounding mode | |
| selected by `%flag_fp_rnd_mode` and the modifiers present in the intrinsic | |
| name. They correspond directly to the `add` PTX instruction. | |
| ##### Semantics: | |
| `%flag_fp_rnd_mode` selects the rounding mode applied to the result, see | |
| {ref}`fp-rounding-modes`. | |
| The '`.ftz`' modifier flushes subnormal inputs and results to sign-preserving | |
| zero. | |
| The '`.sat`' modifier performs a saturating addition where the result is | |
| clamped to `[0.0, 1.0]` and `NaN` results are flushed to `+0.0f`. | |
| Not every combination of operand type, rounding mode and modifier maps to a | |
| PTX instruction. The supported combinations are: | |
| ```{list-table} | |
| :widths: 25 25 25 | |
| :header-rows: 1 | |
| * - Operand Type | |
| - Rounding Modes | |
| - Modifiers | |
| * - `half`, `<2 x half>` | |
| - `rn` | |
| - `.ftz`, `.sat` | |
| * - `bfloat`, `<2 x bfloat>` | |
| - `rn` | |
| - None | |
| * - `float` | |
| - `rn`, `rz`, `rp`, `rm` | |
| - `.ftz`, `.sat` | |
| * - `<2 x float>` | |
| - `rn`, `rz`, `rp`, `rm` | |
| - `.ftz` | |
| * - `double` | |
| - `rn`, `rz`, `rp`, `rm` | |
| - None | |
| ``` | |
| #### '`llvm.nvvm.fmul.*`' Intrinsics | |
| ##### Syntax: | |
| This is an overloaded intrinsic of the form: | |
| ```llvm | |
| declare <ty> @llvm.nvvm.fmul{.ftz}{.sat}.<suffix>(<ty> %a, <ty> %b, i32 immarg %flag_fp_rnd_mode) | |
| ``` | |
| where '`<suffix>`' is the mangled suffix of the overloaded type '`<ty>`' and the | |
| '`.ftz`' and '`.sat`' modifiers are optional. The supported variants are: | |
| ```llvm | |
| declare half @llvm.nvvm.fmul{.ftz}{.sat}.f16(half %a, half %b, i32 immarg %flag_fp_rnd_mode) | |
| declare <2 x half> @llvm.nvvm.fmul{.ftz}{.sat}.v2f16(<2 x half> %a, <2 x half> %b, i32 immarg %flag_fp_rnd_mode) | |
| declare bfloat @llvm.nvvm.fmul.bf16(bfloat %a, bfloat %b, i32 immarg %flag_fp_rnd_mode) | |
| declare <2 x bfloat> @llvm.nvvm.fmul.v2bf16(<2 x bfloat> %a, <2 x bfloat> %b, i32 immarg %flag_fp_rnd_mode) | |
| declare float @llvm.nvvm.fmul{.ftz}{.sat}.f32(float %a, float %b, i32 immarg %flag_fp_rnd_mode) | |
| declare <2 x float> @llvm.nvvm.fmul{.ftz}.v2f32(<2 x float> %a, <2 x float> %b, i32 immarg %flag_fp_rnd_mode) | |
| declare double @llvm.nvvm.fmul.f64(double %a, double %b, i32 immarg %flag_fp_rnd_mode) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fmul.*`' intrinsics multiply `%a` and `%b` using the rounding | |
| mode selected by `%flag_fp_rnd_mode` and the modifiers present in the | |
| intrinsic name. They correspond directly to the `mul` PTX instruction. | |
| ##### Semantics: | |
| `%flag_fp_rnd_mode` selects the rounding mode applied to the result, see | |
| {ref}`fp-rounding-modes`. | |
| The '`.ftz`' modifier flushes subnormal inputs and results to sign-preserving | |
| zero. | |
| The '`.sat`' modifier performs a saturating multiplication where the result is | |
| clamped to `[0.0, 1.0]` and `NaN` results are flushed to `+0.0f`. | |
| Not every combination of operand type, rounding mode and modifier maps to a | |
| PTX instruction. The supported combinations are: | |
| ```{list-table} | |
| :widths: 25 25 25 | |
| :header-rows: 1 | |
| * - Operand Type | |
| - Rounding Modes | |
| - Modifiers | |
| * - `half`, `<2 x half>` | |
| - `rn` | |
| - `.ftz`, `.sat` | |
| * - `bfloat`, `<2 x bfloat>` | |
| - `rn` | |
| - None | |
| * - `float` | |
| - `rn`, `rz`, `rp`, `rm` | |
| - `.ftz`, `.sat` | |
| * - `<2 x float>` | |
| - `rn`, `rz`, `rp`, `rm` | |
| - `.ftz` | |
| * - `double` | |
| - `rn`, `rz`, `rp`, `rm` | |
| - None | |
| ``` | |
| #### '`llvm.nvvm.fma.*`' Half-precision Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare half @llvm.nvvm.fma.rn{.ftz}.f16(half %a, half %b, half %c) | |
| declare <2 x half> @llvm.nvvm.fma.rn{.ftz}.f16x2(<2 x half> %a, <2 x half> %b, <2 x half> %c) | |
| declare bfloat @llvm.nvvm.fma.rn.bf16(bfloat %a, bfloat %b, bfloat %c) | |
| declare <2 x bfloat> @llvm.nvvm.fma.rn.bf16x2(<2 x bfloat> %a, <2 x bfloat> %b, <2 x bfloat> %c) | |
| declare half @llvm.nvvm.fma.rn{.ftz}.sat.f16(half %a, half %b, half %c) | |
| declare <2 x half> @llvm.nvvm.fma.rn{.ftz}.sat.f16x2(<2 x half> %a, <2 x half> %b, <2 x half> %c) | |
| declare half @llvm.nvvm.fma.rn{.ftz}.relu.f16(half %a, half %b, half %c) | |
| declare <2 x half> @llvm.nvvm.fma.rn{.ftz}.relu.f16x2(<2 x half> %a, <2 x half> %b, <2 x half> %c) | |
| declare bfloat @llvm.nvvm.fma.rn.relu.bf16(bfloat %a, bfloat %b, bfloat %c) | |
| declare <2 x bfloat> @llvm.nvvm.fma.rn.relu.bf16x2(<2 x bfloat> %a, <2 x bfloat> %b, <2 x bfloat> %c) | |
| declare half @llvm.nvvm.fma.rn.oob{.relu}.f16(half %a, half %b, half %c) | |
| declare <2 x half> @llvm.nvvm.fma.rn.oob{.relu}.v2f16(<2 x half> %a, <2 x half> %b, <2 x half> %c) | |
| declare bfloat @llvm.nvvm.fma.rn.oob{.relu}.bf16(bfloat %a, bfloat %b, bfloat %c) | |
| declare <2 x bfloat> @llvm.nvvm.fma.rn.oob{.relu}.v2bf16(<2 x bfloat> %a, <2 x bfloat> %b, <2 x bfloat> %c) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fma.*`' intrinsics perform a fused multiply-add with no loss | |
| of precision in the intermediate product and addition. | |
| ##### Semantics: | |
| The '`.sat`' modifier performs a saturating operation where the result is | |
| clamped to `[0.0, 1.0]` and `NaN` results are flushed to `+0.0f`. | |
| The '`.ftz`' modifier flushes subnormal inputs and results to sign-preserving | |
| zero. | |
| The '`.relu`' modifier clamps the result to `0` if negative and `NaN` | |
| results are flushed to canonical `NaN`. | |
| The '`.oob`' modifier clamps the result to `0` if either of the operands is | |
| an `OOB NaN` (defined under [Tensors](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensors)) value. | |
| ### Bit Manipulation Intrinsics | |
| #### '`llvm.nvvm.fshl.clamp.*`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.fshl.clamp.i32(i32 %hi, i32 %lo, i32 %n) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fshl.clamp`' family of intrinsics performs a clamped funnel | |
| shift left. These intrinsics are very similar to '`llvm.fshl`', except the | |
| shift amount is clamped at the integer width (instead of modulo it). Currently, | |
| only `i32` is supported. | |
| ##### Semantics: | |
| The '`llvm.nvvm.fshl.clamp`' family of intrinsic functions performs a clamped | |
| funnel shift left: the first two values are concatenated as { %hi : %lo } (%hi | |
| is the most significant bits of the wide value), the combined value is shifted | |
| left, and the most significant bits are extracted to produce a result that is | |
| the same size as the original arguments. The shift amount is the minimum of the | |
| value of %n and the bit width of the integer type. | |
| #### '`llvm.nvvm.fshr.clamp.*`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.fshr.clamp.i32(i32 %hi, i32 %lo, i32 %n) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.fshr.clamp`' family of intrinsics perform a clamped funnel | |
| shift right. These intrinsics are very similar to '`llvm.fshr`', except the | |
| shift amount is clamped at the integer width (instead of modulo it). Currently, | |
| only `i32` is supported. | |
| ##### Semantics: | |
| The '`llvm.nvvm.fshr.clamp`' family of intrinsic functions performs a clamped | |
| funnel shift right: the first two values are concatenated as { %hi : %lo } (%hi | |
| is the most significant bits of the wide value), the combined value is shifted | |
| right, and the least significant bits are extracted to produce a result that is | |
| the same size as the original arguments. The shift amount is the minimum of the | |
| value of %n and the bit width of the integer type. | |
| #### '`llvm.nvvm.flo.u.*`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.flo.u.i32(i32 %a, i1 %shiftamt) | |
| declare i32 @llvm.nvvm.flo.u.i64(i64 %a, i1 %shiftamt) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.flo.u`' family of intrinsics identifies the bit position of | |
| the leading one, returning either it's offset from the most or least significant | |
| bit. | |
| ##### Semantics: | |
| The '`llvm.nvvm.flo.u`' family of intrinsics returns the bit position of the | |
| most significant 1. If %shiftamt is true, The result is the shift amount needed | |
| to left-shift the found bit into the most-significant bit position, otherwise | |
| the result is the shift amount needed to right-shift the found bit into the | |
| least-significant bit position. 0xffffffff is returned if no 1 bit is found. | |
| #### '`llvm.nvvm.flo.s.*`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.flo.s.i32(i32 %a, i1 %shiftamt) | |
| declare i32 @llvm.nvvm.flo.s.i64(i64 %a, i1 %shiftamt) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.flo.s`' family of intrinsics identifies the bit position of | |
| the leading non-sign bit, returning either it's offset from the most or least | |
| significant bit. | |
| ##### Semantics: | |
| The '`llvm.nvvm.flo.s`' family of intrinsics returns the bit position of the | |
| most significant 0 for negative inputs and the most significant 1 for | |
| non-negative inputs. If %shiftamt is true, The result is the shift amount needed | |
| to left-shift the found bit into the most-significant bit position, otherwise | |
| the result is the shift amount needed to right-shift the found bit into the | |
| least-significant bit position. 0xffffffff is returned if no 1 bit is found. | |
| #### '`llvm.nvvm.{zext,sext}.{wrap,clamp}`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.zext.wrap(i32 %a, i32 %b) | |
| declare i32 @llvm.nvvm.zext.clamp(i32 %a, i32 %b) | |
| declare i32 @llvm.nvvm.sext.wrap(i32 %a, i32 %b) | |
| declare i32 @llvm.nvvm.sext.clamp(i32 %a, i32 %b) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.{zext,sext}.{wrap,clamp}`' family of intrinsics extracts the | |
| low bits of the input value, and zero- or sign-extends them back to the original | |
| width. | |
| ##### Semantics: | |
| The '`llvm.nvvm.{zext,sext}.{wrap,clamp}`' family of intrinsics returns | |
| extension of N lowest bits of operand %a. For the '`wrap`' variants, N is the | |
| value of operand %b modulo 32. For the '`clamp`' variants, N is the value of | |
| operand %b clamped to the range [0, 32]. The N lowest bits are then | |
| zero-extended the case of the '`zext`' variants, or sign-extended the case of | |
| the '`sext`' variants. If N is 0, the result is 0. | |
| #### '`llvm.nvvm.bmsk.{wrap,clamp}`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.bmsk.wrap(i32 %a, i32 %b) | |
| declare i32 @llvm.nvvm.bmsk.clamp(i32 %a, i32 %b) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.bmsk.{wrap,clamp}`' family of intrinsics creates a bit mask | |
| given a starting bit position and a bit width. | |
| ##### Semantics: | |
| The '`llvm.nvvm.bmsk.{wrap,clamp}`' family of intrinsics returns a value with | |
| all bits set to 0 except for %b bits starting at bit position %a. For the | |
| '`wrap`' variants, the values of %a and %b modulo 32 are used. For the | |
| '`clamp`' variants, the values of %a and %b are clamped to the range [0, 32], | |
| which in practice is equivalent to using them as is. | |
| #### '`llvm.nvvm.prmt`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.prmt(i32 %lo, i32 %hi, i32 %selector) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.prmt`' constructs a permutation of the bytes of the first two | |
| operands, selecting based on the third operand. | |
| ##### Semantics: | |
| The bytes in the first two source operands are numbered from 0 to 7: | |
| `{%hi, %lo} = {{b7, b6, b5, b4}, {b3, b2, b1, b0}}`. For each byte in the | |
| target register, a 4-bit selection value is defined. | |
| The 3 lsbs of the selection value specify which of the 8 source bytes should be | |
| moved into the target position. The msb defines if the byte value should be | |
| copied, or if the sign (msb of the byte) should be replicated over all 8 bits | |
| of the target position (sign extend of the byte value); msb=0 means copy the | |
| literal value; msb=1 means replicate the sign. | |
| These 4-bit selection values are pulled from the lower 16-bits of the %selector | |
| operand, with the least significant selection value corresponding to the least | |
| significant byte of the destination. | |
| #### '`llvm.nvvm.prmt.*`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.prmt.f4e(i32 %lo, i32 %hi, i32 %selector) | |
| declare i32 @llvm.nvvm.prmt.b4e(i32 %lo, i32 %hi, i32 %selector) | |
| declare i32 @llvm.nvvm.prmt.rc8(i32 %lo, i32 %selector) | |
| declare i32 @llvm.nvvm.prmt.ecl(i32 %lo, i32 %selector) | |
| declare i32 @llvm.nvvm.prmt.ecr(i32 %lo, i32 %selector) | |
| declare i32 @llvm.nvvm.prmt.rc16(i32 %lo, i32 %selector) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.prmt.*`' family of intrinsics constructs a permutation of the | |
| bytes of the first one or two operands, selecting based on the 2 least | |
| significant bits of the final operand. | |
| ##### Semantics: | |
| As with the generic '`llvm.nvvm.prmt`' intrinsic, the bytes in the first one | |
| or two source operands are numbered. The first source operand (%lo) is numbered | |
| {b3, b2, b1, b0}, in the case of the '`f4e`' and '`b4e`' variants, the | |
| second source operand (%hi) is numbered {b7, b6, b5, b4}. | |
| Depending on the 2 least significant bits of the %selector operand, the result | |
| of the permutation is defined as follows: | |
| ```{eval-rst} | |
| +------------+----------------+--------------+ | |
| | Mode | %selector[1:0] | Output | | |
| +------------+----------------+--------------+ | |
| | ``f4e`` | 0 | {3, 2, 1, 0} | | |
| | +----------------+--------------+ | |
| | | 1 | {4, 3, 2, 1} | | |
| | +----------------+--------------+ | |
| | | 2 | {5, 4, 3, 2} | | |
| | +----------------+--------------+ | |
| | | 3 | {6, 5, 4, 3} | | |
| +------------+----------------+--------------+ | |
| | ``b4e`` | 0 | {5, 6, 7, 0} | | |
| | +----------------+--------------+ | |
| | | 1 | {6, 7, 0, 1} | | |
| | +----------------+--------------+ | |
| | | 2 | {7, 0, 1, 2} | | |
| | +----------------+--------------+ | |
| | | 3 | {0, 1, 2, 3} | | |
| +------------+----------------+--------------+ | |
| | ``rc8`` | 0 | {0, 0, 0, 0} | | |
| | +----------------+--------------+ | |
| | | 1 | {1, 1, 1, 1} | | |
| | +----------------+--------------+ | |
| | | 2 | {2, 2, 2, 2} | | |
| | +----------------+--------------+ | |
| | | 3 | {3, 3, 3, 3} | | |
| +------------+----------------+--------------+ | |
| | ``ecl`` | 0 | {3, 2, 1, 0} | | |
| | +----------------+--------------+ | |
| | | 1 | {3, 2, 1, 1} | | |
| | +----------------+--------------+ | |
| | | 2 | {3, 2, 2, 2} | | |
| | +----------------+--------------+ | |
| | | 3 | {3, 3, 3, 3} | | |
| +------------+----------------+--------------+ | |
| | ``ecr`` | 0 | {0, 0, 0, 0} | | |
| | +----------------+--------------+ | |
| | | 1 | {1, 1, 1, 0} | | |
| | +----------------+--------------+ | |
| | | 2 | {2, 2, 1, 0} | | |
| | +----------------+--------------+ | |
| | | 3 | {3, 2, 1, 0} | | |
| +------------+----------------+--------------+ | |
| | ``rc16`` | 0 | {1, 0, 1, 0} | | |
| | +----------------+--------------+ | |
| | | 1 | {3, 2, 3, 2} | | |
| | +----------------+--------------+ | |
| | | 2 | {1, 0, 1, 0} | | |
| | +----------------+--------------+ | |
| | | 3 | {3, 2, 3, 2} | | |
| +------------+----------------+--------------+ | |
| ``` | |
| ### TMA family of Intrinsics | |
| #### Tensor-Map Property Overrides | |
| Tensor intrinsics with `override` in their name replace properties in the | |
| opaque tensor map with explicit operands. The supported override variants and | |
| operand layouts are: | |
| | Intrinsic suffix | Modes and dimensions | Operands after `%tensor_map` | | |
| |----------------------------|------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------| | |
| | `override.addr` | `tile` 1D--5D; `im2col`, `im2col.w`, and `im2col.w.128` 3D--5D; `tile.gather4` or `tile.scatter4` 2D | `ptr addrspace(1) %override_addr` | | |
| | `override.addr.dim` | `tile` 1D | `%override_addr, i16 %ts0` | | |
| | `override.addr.dim.stride` | `tile` 2D--5D | `%override_addr, i16 %ts0 ... i16 %ts{N-1}, i32 %stride0 ... i32 %stride{N-2}, i16 %upper_stride` | | |
| `%override_addr` replaces the tensor map's global base address. It must be | |
| 16-byte aligned; otherwise a runtime error is raised. The 128 KiB range | |
| beginning at that address must be allocated and accessible while the | |
| instruction executes; otherwise the behavior is undefined. | |
| `%ts0 ... %ts{N-1}` replace the global dimensions. For | |
| `override.addr.dim.stride`, `%stride0 ... %stride{N-2}` contain bits 4--35 of | |
| the corresponding byte strides and `%upper_stride` contains bits 36--51 of | |
| every stride. Equivalently, stride `i` is | |
| `((zext(%upper_stride) << 32) | zext(%stride{i})) << 4` bytes. Tensor | |
| coordinates must be zero when dimensions or strides are overridden; otherwise | |
| the behavior is undefined. | |
| These variants lower to `.override::global_address`, optionally followed by | |
| `.override::global_dim` or `.override::global_dim_stride`, and require | |
| `sm_107f` and PTX 9.4 or later. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-overriding-tensor-property-value). | |
| #### TMA Global-to-Shared Data-Validity Reporting | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.g2s.*`' and '`@llvm.nvvm.cp.async.bulk.tensor.g2s.cta.*`' intrinsics, including the property-override | |
| forms, take an `i32 %flag_valid_pattern` immediate argument in the range \[0, 6). The non-tensor '`@llvm.nvvm.cp.async.bulk.global.to.shared.cluster`', '`@llvm.nvvm.cp.async.bulk.global.to.shared.cluster.relaxed`', '`@llvm.nvvm.cp.async.bulk.global.to.shared.cta`', and '`@llvm.nvvm.cp.async.bulk.global.to.shared.cta.relaxed`' intrinsics also take the same `i32 %flag_valid_pattern` immediate argument. It selects the data-validity reporting mechanism applied to the data while it is copied: | |
| | Value | PTX Report Mechanism | Semantics | | |
| |-------|---------------------------------------------------------|------------------------------------------------------------------| | |
| | 0 | *disabled; qualifier omitted* | No data inspection (default) | | |
| | 1 | `.mbarrier::report::validity::per_16bytes::80000000` | Sample 4 FP32 values per aligned 16-byte chunk and match `-0` | | |
| | 2 | `.mbarrier::report::validity::per_16bytes::8000` | Sample 8 FP16 values per aligned 16-byte chunk and match `-0` | | |
| | 3 | `.mbarrier::report::validity::per_16bytes::80` | Sample 16 FP8 values per aligned 16-byte chunk and match `-0` | | |
| | 4 | `.mbarrier::report::validity::per_16bytes::8` | Sample 32 FP4 values per aligned 16-byte chunk and match `-0` | | |
| | 5 | `.mbarrier::report::validity::per_element::ff` | Inspect each 8-/4-bit element for the `ff` pattern | | |
| When the reporting mechanism is not specified, it defaults to disabled, which emits no qualifier in the PTX instruction and maintains compatibility with older PTX versions. For values 1 through 5, the associated mbarrier must be initialized with `layout::v1` layout; otherwise, the behavior is undefined. | |
| The nonzero report mechanism requires `sm_107f` or later in the same family and is supported only on PTX 9.4 or later. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.global.to.shared.cluster`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.global.to.shared.cluster.i16(ptr addrspace(7) %dst, ptr addrspace(3) %mbar, ptr addrspace(1) %src, i32 %size, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.global.to.shared.cluster.i32(..., i32 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %flag_valid_pattern) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.global.to.shared.cluster`' intrinsic | |
| corresponds to the `cp.async.bulk.shared::cluster.global.*` family of PTX | |
| instructions. These instructions initiate an asynchronous copy of bulk data from | |
| global memory to shared::cluster memory. The 32-bit operand `%size` specifies | |
| the amount of memory to be copied and it must be a multiple of 16. | |
| - The trailing `%flag_mc`, `%flag_ch`, and `%flag_valid_pattern` arguments control | |
| the multicast and cache-hint modifiers and the data-validity reporting | |
| pattern. They must be compile-time constants. | |
| - The argument denoted by `i1 %flag_ch`, when set, indicates a valid cache_hint | |
| (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX | |
| instruction. | |
| - The argument denoted by `i1 %flag_mc`, when set, indicates the presence of a | |
| multicast mask (`%mc`) and generates the PTX instruction with the | |
| `.multicast::cluster` modifier. An `i16` mask selects the default/16-bit | |
| multicast form while an `i32` mask selects the `.multicast::cluster::32b` | |
| variant. Only `i16` and `i32` mask types are supported. The 32-bit form | |
| requires `sm_107f` or later in the same family and is supported only on PTX | |
| 9.4 or later. | |
| - The trailing `i32 %flag_valid_pattern` flag selects the data-validity reporting | |
| mechanism; see | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk). | |
| #### '`llvm.nvvm.cp.async.bulk.global.to.shared.cluster.relaxed`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.global.to.shared.cluster.relaxed.i16(ptr addrspace(7) %dst, ptr addrspace(3) %mbar, ptr addrspace(1) %src, i32 %size, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %flag_scope, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.global.to.shared.cluster.relaxed.i32(..., i32 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %flag_scope, i32 %flag_valid_pattern) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.global.to.shared.cluster.relaxed`' intrinsic | |
| corresponds to the | |
| `cp.async.bulk.relaxed.<scope>.shared::cluster.global.mbarrier::complete_tx::bytes{...}.b128` | |
| family of PTX instructions. These instructions initiate an asynchronous copy of bulk data from | |
| global memory to shared::cluster memory, like the weak variant above. Unlike | |
| the weak variant, the copy accesses memory with naturally-aligned strong | |
| memory operations with element-wise atomic size specified by the `.b128` type (implicit) and | |
| thread scope specified by `%flag_scope`; the complete-tx operation on the mbarrier has `.release` semantics at the | |
| `.cluster` scope. | |
| - The trailing `%flag_mc`, `%flag_ch`, `%flag_scope`, and `%flag_valid_pattern` | |
| arguments control the multicast and cache-hint modifiers, the scope of the | |
| relaxed memory ordering semantics, and the data-validity reporting pattern. | |
| They must be compile-time constants. | |
| - The argument denoted by `i1 %flag_ch`, when set, indicates a valid cache_hint | |
| (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX | |
| instruction. | |
| - The argument denoted by `i1 %flag_mc`, when set, indicates the presence of a | |
| multicast mask (`%mc`) and generates the PTX instruction with the | |
| `.multicast::cluster` modifier. An `i16` mask selects the default/16-bit | |
| multicast form while an `i32` mask selects the `.multicast::cluster::32b` | |
| variant. Only `i16` and `i32` mask types are supported. | |
| - The `i32 %flag_scope` argument takes values within the range \[0, 4) and selects | |
| the `.scope` qualifier of the PTX instruction: | |
| | Value | `flag_scope` | | |
| |-------|--------------| | |
| | 0 | `.cta` | | |
| | 1 | `.cluster` | | |
| | 2 | `.gpu` | | |
| | 3 | `.sys` | | |
| - The trailing `i32 %flag_valid_pattern` flag selects the data-validity reporting | |
| mechanism; see | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk). | |
| #### '`llvm.nvvm.cp.async.bulk.global.to.shared.cta`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.global.to.shared.cta(ptr addrspace(3) %dst, ptr addrspace(3) %mbar, ptr addrspace(1) %src, i32 %size, i32 %ibl, i32 %ibr, i64 %ch, i1 %flag_ch, i1 %flag_oob, i32 %flag_valid_pattern) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.global.to.shared.cta`' intrinsic corresponds to | |
| the `cp.async.bulk.shared::cta.global.*` family of PTX instructions. These | |
| instructions initiate an asynchronous copy of bulk data from global memory to | |
| shared::cta memory. The 32-bit operand `%size` specifies the amount of memory | |
| to be copied and it must be a multiple of 16. | |
| - The trailing `%flag_ch`, `%flag_oob`, and `%flag_valid_pattern` arguments | |
| control the cache-hint modifier, the out-of-bounds handling, and the | |
| data-validity reporting pattern. They must be compile-time constants. | |
| - The argument denoted by `i1 %flag_ch`, when set, indicates a valid cache_hint | |
| (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX | |
| instruction. | |
| - The argument denoted by `i1 %flag_oob`, when set, generates the | |
| `.ignore_oob` variant of the PTX instruction. In that case, the `i32 %ibl` | |
| and `i32 %ibr` operands specify the numbers of bytes to ignore on the left | |
| and right sides of the source, respectively. Both operands must be in the | |
| range \[0, 15]; otherwise, the behavior is undefined. The `.ignore_oob` | |
| modifier requires PTX ISA 9.2 or later. When `%flag_oob` is set, | |
| `%flag_valid_pattern` must be 0 (data-validity reporting disabled). | |
| - The trailing `i32 %flag_valid_pattern` flag selects the data-validity reporting | |
| mechanism; see | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk). | |
| #### '`llvm.nvvm.cp.async.bulk.global.to.shared.cta.relaxed`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.global.to.shared.cta.relaxed(ptr addrspace(3) %dst, ptr addrspace(3) %mbar, ptr addrspace(1) %src, i32 %size, i32 %ibl, i32 %ibr, i64 %ch, i1 %flag_ch, i1 %flag_oob, i32 %flag_scope, i32 %flag_valid_pattern) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.global.to.shared.cta.relaxed`' intrinsic | |
| corresponds to the | |
| `cp.async.bulk.relaxed.<scope>.shared::cta.global.mbarrier::complete_tx::bytes{...}.b128` | |
| family of PTX instructions. These instructions initiate an asynchronous copy of bulk data from | |
| global memory to shared::cta memory, like the weak variant above. Unlike | |
| the weak variant, the copy accesses memory with naturally-aligned strong | |
| memory operations with element-wise atomic size specified by the `.b128` type (implicit) and | |
| thread scope specified by `%flag_scope`; the complete-tx operation on the mbarrier has `.release` semantics at the | |
| `.cluster` scope. The `.relaxed` variants require PTX ISA 9.3 or later and | |
| `sm_90a`, `sm_100f`, or `sm_110f` or later in the same family. | |
| - The trailing `%flag_ch`, `%flag_oob`, `%flag_scope`, and `%flag_valid_pattern` | |
| arguments control the cache-hint modifier, the out-of-bounds handling, the | |
| scope of the relaxed memory ordering semantics, and the data-validity | |
| reporting pattern. They must be compile-time constants. | |
| - The argument denoted by `i1 %flag_ch`, when set, indicates a valid cache_hint | |
| (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX | |
| instruction. | |
| - The argument denoted by `i1 %flag_oob`, when set, generates the | |
| `.ignore_oob` variant of the PTX instruction. In that case, the `i32 %ibl` | |
| and `i32 %ibr` operands specify the numbers of bytes to ignore on the left | |
| and right sides of the source, respectively. Both operands must be in the | |
| range \[0, 15]; otherwise, the behavior is undefined. When `%flag_oob` is | |
| set, `%flag_valid_pattern` must be 0 (data-validity reporting disabled); this | |
| mutual exclusion is enforced by the verifier. | |
| - The `i32 %flag_scope` argument takes values within the range \[0, 4) and selects | |
| the `.scope` qualifier of the PTX instruction: | |
| | Value | `flag_scope` | | |
| |-------|--------------| | |
| | 0 | `.cta` | | |
| | 1 | `.cluster` | | |
| | 2 | `.gpu` | | |
| | 3 | `.sys` | | |
| - The trailing `i32 %flag_valid_pattern` flag selects the data-validity reporting | |
| mechanism; see | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk). | |
| #### '`llvm.nvvm.cp.async.bulk.shared.cta.to.global`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.shared.cta.to.global(ptr addrspace(1) %dst, ptr addrspace(3) %src, i32 %size, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.shared.cta.to.global.bytemask(..., i32 %size, i64 %ch, i1 %flag_ch, i16 %mask) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.shared.cta.to.global`' intrinsic corresponds to | |
| the `cp.async.bulk.global.shared::cta.*` set of PTX instructions. These | |
| instructions initiate an asynchronous copy from shared::cta to global memory. | |
| The 32-bit operand `%size` specifies the amount of memory to be copied | |
| (in bytes) and it must be a multiple of 16. For the `.bytemask` variant, the | |
| 16-bit wide mask operand specifies whether the i-th byte of each 16-byte wide | |
| chunk of source data is copied to the destination. | |
| - The `i1 %flag_ch` argument to these intrinsics is a boolean flag indicating | |
| support for cache_hint. This flag argument must be a compile-time constant. | |
| When set, it indicates a valid cache_hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk). | |
| #### '`llvm.nvvm.cp.async.bulk.shared.cta.to.cluster`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.shared.cta.to.cluster(ptr addrspace(7) %dst, ptr addrspace(3) %mbar, ptr addrspace(3) %src, i32 %size) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.shared.cta.to.cluster`' intrinsic corresponds | |
| to the `cp.async.bulk.shared::cluster.shared::cta.*` PTX instruction. This | |
| instruction initiates an asynchronous copy from shared::cta to shared::cluster | |
| memory. The destination has to be in the shared memory of a different CTA within | |
| the cluster. The 32-bit operand `%size` specifies the amount of memory to be | |
| copied and it must be a multiple of 16. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk). | |
| #### '`llvm.nvvm.cp.async.bulk.prefetch.L2`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.prefetch.L2(ptr addrspace(1) %src, i32 %size, i64 %ch, i1 %flag_ch) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.prefetch.L2`' intrinsic corresponds to the | |
| `cp.async.bulk.prefetch.L2.*` family of PTX instructions. These instructions | |
| initiate an asynchronous prefetch of bulk data from global memory to the L2 | |
| cache. The 32-bit operand `%size` specifies the amount of memory to be | |
| prefetched in terms of bytes and it must be a multiple of 16. | |
| - The last argument to these intrinsics is boolean flag indicating support for | |
| cache_hint. These flag argument must be compile-time constant. When set, it | |
| indicates a valid cache_hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch). | |
| #### '`llvm.nvvm.cp.async.bulk.prefetch.evict.priority`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.prefetch.evict.priority(ptr addrspace(1) %src, i32 %size, i32 %evict_policy) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.prefetch.evict.priority`' intrinsic | |
| asynchronously prefetches `%size` bytes from global memory into the L2 cache | |
| using the selected eviction priority. It lowers to the | |
| `cp.async.bulk.prefetch.L2.global` PTX instruction with an L2 | |
| eviction-priority qualifier. Unlike `@llvm.nvvm.cp.async.bulk.prefetch.L2`, | |
| it takes an eviction policy instead of a cache hint. `%size` must be a | |
| multiple of 16. | |
| The trailing `i32 %evict_policy` is an immediate (`immarg`) argument that | |
| selects the L2 eviction policy. | |
| | Value | Name | PTX qualifier | | |
| |-------|----------------|---------------------| | |
| | 0 | `evict_normal` | (none, the default) | | |
| | 1 | `evict_last` | `.L2::evict_last` | | |
| This intrinsic requires `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.1d(ptr %tensor_map, i32 %d0, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.gather4.2d(ptr %tensor_map, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i32 %evict_policy) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously prefetch tile-mode tensor data from global | |
| memory into the L2 cache using the selected eviction priority. They are the | |
| eviction-priority forms of the corresponding | |
| `@llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.*` intrinsics, and the | |
| trailing `i32 %evict_policy` uses the encoding described for | |
| `@llvm.nvvm.cp.async.bulk.prefetch.evict.priority`. They do not take cache-hint | |
| operands and require `sm_107f` and PTX 9.4 or later. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.128.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.128.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.128.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously prefetch im2col-mode tensor data from global | |
| memory into the L2 cache using the selected eviction priority. An N-dimensional | |
| form takes N `i32` tensor coordinates. The `im2col` forms take N - 2 `i16` | |
| im2col offsets, while the `im2col.w` and `im2col.w.128` forms take | |
| `i16 %wHalo` and `i16 %wOffset`. | |
| The trailing `i32 %evict_policy` uses the encoding described for | |
| `@llvm.nvvm.cp.async.bulk.prefetch.evict.priority`. These forms do not take | |
| cache-hint operands and require `sm_107f` and PTX 9.4 or later. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override*.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.1d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i64 %cache_hint, i1 %flag_cache_hint) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.gather4.override.addr.2d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i64 %cache_hint, i1 %flag_cache_hint) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.dim.1d(ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i64 %cache_hint, i1 %flag_cache_hint) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.dim.stride.2d(ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i64 %cache_hint, i1 %flag_cache_hint) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.dim.stride.3d(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.dim.stride.4d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.override.addr.dim.stride.5d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously prefetch tile-mode tensor data from global | |
| memory into the L2 cache while overriding tensor-map properties with explicit | |
| operands. They lower to the | |
| `cp.async.bulk.prefetch.tensor.<N>d.L2.global.tile` PTX instructions | |
| qualified with `.override::*`. | |
| The supported override variants and operands are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| - The last argument is a compile-time boolean indicating whether `%cache_hint` | |
| is valid. When set, it indicates a valid cache_hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.override.addr.3d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i64 %cache_hint, i1 %flag_cache_hint) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.override.addr.4d(..., i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.override.addr.5d(..., i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.override.addr.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.128.override.addr.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously prefetch im2col-mode tensor data from global | |
| memory into the L2 cache while overriding the tensor-map global address. An | |
| N-dimensional form takes N `i32` tensor coordinates. The `im2col` forms take | |
| N - 2 `i16` im2col offsets, while the `im2col.w` and `im2col.w.128` forms take | |
| `i16 %wHalo` and `i16 %wOffset`. | |
| `%override_addr` and its requirements are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). The trailing | |
| `i1 %flag_cache_hint` must be a compile-time constant; when set, it marks | |
| `i64 %cache_hint` as valid and emits the `.L2::cache_hint` PTX qualifier. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override*.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.1d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.dim.1d(ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.dim.stride.2d(ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.dim.stride.3d(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.dim.stride.4d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.override.addr.dim.stride.5d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.tile.gather4.override.addr.2d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i32 %evict_policy) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously prefetch tile-mode tensor data from global | |
| memory into the L2 cache using the selected eviction priority while overriding | |
| tensor-map properties. They are the eviction-priority forms of the | |
| corresponding tensor prefetch override intrinsics. The override operands are | |
| described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). The remaining | |
| operands use the coordinate layout of the corresponding tile or | |
| `tile.gather4` intrinsic. | |
| The trailing `i32 %evict_policy` is an immediate argument: 0 selects the | |
| default `evict_normal` policy and emits no PTX qualifier; 1 selects | |
| `.L2::evict_last`. These forms do not take cache-hint operands. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.override.addr.3d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i32 %evict_policy) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.override.addr.4d(..., i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.override.addr.5d(..., i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.override.addr.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.evict.priority.im2col.w.128.override.addr.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously prefetch im2col-mode tensor data from global | |
| memory into the L2 cache using the selected eviction priority while overriding | |
| the tensor-map global address. An N-dimensional form takes N `i32` tensor | |
| coordinates. The `im2col` forms take N - 2 `i16` im2col offsets, while the | |
| `im2col.w` and `im2col.w.128` forms take `i16 %wHalo` and `i16 %wOffset`. | |
| `%override_addr` and its requirements are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). The trailing | |
| `i32 %evict_policy` uses the encoding described for | |
| `@llvm.nvvm.cp.async.bulk.prefetch.evict.priority`; these forms do not take | |
| cache-hint operands. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.prefetch.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.prefetch.global.L1(ptr addrspace(1) %global_ptr) | |
| declare void @llvm.nvvm.prefetch.global.L2(ptr addrspace(1) %global_ptr) | |
| declare void @llvm.nvvm.prefetch.local.L1(ptr addrspace(5) %local_ptr) | |
| declare void @llvm.nvvm.prefetch.local.L2(ptr addrspace(5) %local_ptr) | |
| declare void @llvm.nvvm.prefetch.L1(ptr %ptr) | |
| declare void @llvm.nvvm.prefetch.L2(ptr %ptr) | |
| declare void @llvm.nvvm.prefetch.L1.32B.valid_addr.p0(ptr %generic_ptr) | |
| declare void @llvm.nvvm.prefetch.L1.32B.valid_addr.p1(ptr addrspace(1) %global_ptr) | |
| declare void @llvm.nvvm.prefetch.tensormap.p0(ptr %ptr) | |
| declare void @llvm.nvvm.prefetch.tensormap.p4(ptr addrspace(4) %const_ptr) | |
| declare void @llvm.nvvm.prefetch.tensormap.p101(ptr addrspace(101) %param_ptr) | |
| declare void @llvm.nvvm.prefetch.global.L2.evict.normal(ptr addrspace(1) %global_ptr) | |
| declare void @llvm.nvvm.prefetch.global.L2.evict.last(ptr addrspace(1) %global_ptr) | |
| declare void @llvm.nvvm.prefetchu.L1(ptr %ptr) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.prefetch.*`' and '`@llvm.nvvm.prefetchu.*`' intrinsic | |
| correspond to the '`prefetch.*`;' and '`prefetchu.*`' family of PTX | |
| instructions. The '`prefetch.*`' instructions bring the cache line containing | |
| the specified address in global or local memory address space into the | |
| specified cache level (L1 or L2). The `L1.32B.valid_addr` intrinsic variants | |
| request a prefetch of at least 32 bytes. This qualifier can be used with a | |
| `.global` specifier or with no specifier, indicating a generic address, which | |
| must fall within the `global` state space. At least 1B of the memory location | |
| specified by the argument must be valid, otherwise the behavior is undefined. | |
| If the '`.tensormap`' qualifier is specified then the prefetch instruction | |
| brings the cache line containing the specified address in the '`.const`' or | |
| '`.param memory`' state space for subsequent use by the | |
| '`cp.async.bulk.tensor`' instruction. The '`prefetchu.*`' instruction brings | |
| the cache line containing the specified generic address into the specified | |
| uniform cache level. If no address space is specified, it is assumed to be | |
| generic address. The intrinsic uses and eviction priority which can be accessed | |
| by the '`.level::eviction_priority`' modifier. | |
| - A prefetch to a shared memory location performs no operation. | |
| - A prefetch into the uniform cache requires a generic address, and no operation | |
| occurs if the address maps to a const, local, or shared memory location. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-prefetch-prefetchu). | |
| #### '`llvm.nvvm.applypriority.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.applypriority.global.L2.evict.normal(ptr addrspace(1) %global_ptr, i64 %size) | |
| declare void @llvm.nvvm.applypriority.L2.evict.normal(ptr %ptr, i64 %size) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.applypriority.*`' applies the cache eviction priority | |
| specified by the .level::eviction_priority qualifier to the address range | |
| \[a..a+size) in the specified cache level. If no state space is specified then | |
| generic addressing is used. If the specified address does not fall within the | |
| address window of .global state space then the behavior is undefined. The | |
| operand size is an integer constant that specifies the amount of data, in bytes, | |
| in the specified cache level on which the priority is to be applied. The only | |
| supported value for the size operand is 128. | |
| #### '`llvm.nvvm.applypriority.async.bulk.evict.priority`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.applypriority.async.bulk.evict.priority(ptr addrspace(1) %global_ptr, i32 %size, i32 %evict_policy) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.applypriority.async.bulk.evict.priority`' intrinsic applies | |
| the selected L2 eviction priority to `[%global_ptr, %global_ptr + %size)`. | |
| It lowers to the `applypriority.async.bulk.global.bulk_group` PTX instruction. | |
| `%size` may be a register or an immediate and must be a multiple of 16. | |
| `%global_ptr` must be aligned to 128 bytes. | |
| `%evict_policy` is an immediate argument. Its only valid value is 0, which | |
| selects `.L2::evict_normal`. | |
| This intrinsic requires `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.1d(ptr %tensor_map, i32 %d0, i32 %evict_policy) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.gather4.2d(ptr %tensor_map, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i32 %evict_policy) | |
| ``` | |
| ##### Overview: | |
| These intrinsics apply `evict_normal` priority to a tile tensor region. An | |
| N-dimensional tile form takes N `i32` tensor coordinates; the `tile.gather4` | |
| form takes `i32 %x0` followed by four row coordinates `%y0 ... %y3`. The | |
| trailing `%evict_policy` is an immediate argument whose only valid value is 0. | |
| They require `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i32 %evict_policy) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.4d(..., i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.5d(..., i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.w.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.w.128.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics apply `evict_normal` priority to an im2col tensor region | |
| described by `%tensor_map`. An N-dimensional form takes N `i32` tensor | |
| coordinates. The `im2col` forms take N - 2 `i16` im2col offsets, while the | |
| `im2col.w` and `im2col.w.128` forms take `i16 %wHalo` and `i16 %wOffset`. | |
| The trailing `%evict_policy` is an immediate argument whose only valid value is | |
| 0. These intrinsics require `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override*.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.1d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %evict_policy) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.dim.1d(ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i32 %evict_policy) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.dim.stride.2d(ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i32 %evict_policy) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.dim.stride.3d(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.dim.stride.4d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.override.addr.dim.stride.5d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.tile.gather4.override.addr.2d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i32 %evict_policy) | |
| ``` | |
| ##### Overview: | |
| These intrinsics apply the selected L2 eviction priority to a tile region while | |
| overriding tensor-map properties with explicit operands. Their supported | |
| override variants and operands are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). An | |
| N-dimensional tile form takes N `i32` tensor coordinates; the `tile.gather4` | |
| form takes `i32 %x0` followed by four row coordinates `%y0 ... %y3`. | |
| `%evict_policy` is an immediate argument whose only valid value is 0 | |
| (`evict_normal`). | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-applypriority). | |
| #### '`llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.override.addr.3d(ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i32 %evict_policy) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.override.addr.4d(..., i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.override.addr.5d(..., i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.w.override.addr.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| declare void @llvm.nvvm.applypriority.async.bulk.tensor.evict.priority.im2col.w.128.override.addr.{3,4,5}d(..., i16 %wHalo, i16 %wOffset, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics apply the selected L2 eviction priority to an im2col region | |
| while overriding the tensor-map global address. An N-dimensional form takes N | |
| `i32` tensor coordinates. The `im2col` forms take N - 2 `i16` im2col offsets, | |
| while the `im2col.w` and `im2col.w.128` forms take `i16 %wHalo` and | |
| `i16 %wOffset`. | |
| `%override_addr` and its requirements are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| `%evict_policy` is an immediate argument whose only valid value is 0 | |
| (`evict_normal`). | |
| #### `llvm.nvvm.discard.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.discard.global.L2(ptr addrspace(1) %global_ptr, i64 immarg) | |
| declare void @llvm.nvvm.discard.L2(ptr %ptr, i64 immarg) | |
| ``` | |
| ##### Overview: | |
| The *effects* of the `@llvm.nvvm.discard.L2*` intrinsics are those of a | |
| non-atomic non-volatile `llvm.memset` that writes `undef` to the destination | |
| address range `[%ptr, %ptr + immarg)`. The `%ptr` must be aligned by 128 | |
| bytes. Subsequent reads from the address range may read `undef` until the | |
| memory is overwritten with a different value. These operations *hint* the | |
| implementation that data in the L2 cache can be destructively discarded without | |
| writing it back to memory. The operand `immarg` is an integer constant that | |
| specifies the length in bytes of the address range `[%ptr, %ptr + immarg)` to | |
| write `undef` into. The only supported value for the `immarg` operand is | |
| `128`. If generic addressing is used and the specified address does not fall | |
| within the address window of global memory (`addrspace(1)`) the behavior is | |
| undefined. | |
| ```llvm | |
| call void @llvm.nvvm.discard.L2(ptr %p, i64 128) ;; writes `undef` to [p, p+128) | |
| %a = load i64, ptr %p. ;; loads 8 bytes containing undef | |
| %b = load i64, ptr %p ;; loads 8 bytes containing undef | |
| ;; comparing %a and %b compares `undef` values! | |
| %fa = freeze i64 %a ;; freezes undef to stable bit-pattern | |
| %fb = freeze i64 %b ;; freezes undef to stable bit-pattern | |
| ;; %fa may compare different to %fb! | |
| ``` | |
| For more information, refer to the [CUDA C++ discard documentation](https://nvidia.github.io/cccl/libcudacxx/extended_api/memory_access_properties/discard_memory.html) | |
| and to the [PTX ISA discard documentation](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-discard) . | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.1d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.1d.i32(..., i32 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.2d.{i16,i32}(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.3d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.gather4.2d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.gather4.2d.i32(..., i32 %mc, i64 %ch, i1 %flag_mc, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.g2s.tile.[1-5]d`' intrinsics correspond | |
| to the `cp.async.bulk.tensor.[1-5]d.*` set of PTX instructions. These | |
| instructions initiate an asynchronous copy of tensor data from global memory to | |
| shared::cluster memory (indicated by the `g2s` prefix) in `tile` mode. In | |
| tile mode, the multi-dimensional layout of the source tensor is preserved at the | |
| destination. The dimension of the tensor data ranges from 1d to 5d with the | |
| coordinates specified by the `i32 %d0 ... i32 %d4` arguments. In | |
| `tile.gather4` mode, four rows in a 2D tensor are combined to form a single 2D | |
| destination tensor. The first coordinate `i32 %x0` denotes the column index | |
| followed by four coordinates indicating the four row-indices. So, this mode | |
| takes a total of 5 coordinates as input arguments. For more information on | |
| `gather4` mode, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-tiled-scatter4-gather4-modes). | |
| - The last four operands control the multicast and cache-hint modifiers, the | |
| CTA group, and the data-validity reporting pattern. They must be compile-time | |
| constants. The backend uses them to select the appropriate instruction. | |
| - The argument denoted by `i1 %flag_ch`, when set, indicates a valid cache_hint | |
| (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX | |
| instruction. | |
| - The argument denoted by `i1 %flag_mc`, when set, indicates the presence of a | |
| multicast mask (`%mc`) and generates the PTX instruction with the | |
| `.multicast::cluster` modifier. An `i16` mask selects the default/16-bit | |
| multicast form while an `i32` mask selects the `.multicast::cluster::32b` | |
| variant. Only `i16` and `i32` mask types are supported. The 32-bit form is | |
| supported only on `sm_107f` or later in the same family and is supported only | |
| on the PTX 9.4 or later. | |
| - The argument denoted by `i32 %cta_group` takes values within the range | |
| \[0, 3). The default value is '0' emits no cta_group modifier in the instruction. | |
| The values '1' and '2' lower to `cta_group::1` and `cta_group::2` variants of the PTX instruction | |
| respectively. | |
| - The last `i32 %flag_valid_pattern` flag selects the data-validity reporting | |
| mechanism; see | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.3d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.3d.i32(..., i16 %im2col0, i32 %mc, i64 %ch, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.3d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.3d.i32(..., i16 %im2col0, i32 %mc, i64 %ch, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.3d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.3d.i32(..., i16 %im2col0, i32 %mc, i64 %ch, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.[3-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.tensor.[1-5]d.*` set of PTX instructions. | |
| These instructions initiate an asynchronous copy of tensor data from global | |
| memory to shared::cluster memory (indicated by the `g2s` prefix) in `im2col` | |
| mode. In im2col mode, some dimensions of the source tensor are unrolled into a | |
| single dimensional column at the destination. In this mode, the tensor has to be | |
| at least three-dimensional. Along with the tensor coordinates, im2col offsets | |
| are also specified (denoted by `i16 im2col0...i16 %im2col2`). For the | |
| `im2col` mode, the number of offsets is two less than the number of dimensions | |
| of the tensor operation. For the `im2col.w` and `im2col.w.128` mode, the | |
| number of offsets is always 2, denoted by `i16 %wHalo` and `i16 %wOffset` | |
| arguments. For more information on `im2col.w` and `im2col.w.128` modes, | |
| refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-im2col-w-w128-modes). | |
| The last four arguments to these intrinsics are flags, with the same | |
| functionality as described in the `tile` mode intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.1d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.gather4.2d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.[1-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.tensor.[1-5]d.shared::cta.global.*` set of | |
| PTX instructions. These instructions initiate an asynchronous copy of tensor | |
| data from global memory to shared::cta memory in `tile` mode. In tile mode, | |
| the multi-dimensional layout of the source tensor is preserved at the | |
| destination. The dimension of the tensor data ranges from 1d to 5d with the | |
| coordinates specified by the `i32 %d0 ... i32 %d4` arguments. In | |
| `tile.gather4` mode, four rows in a 2D tensor are combined to form a single 2D | |
| destination tensor. The first coordinate `i32 %x0` denotes the column index | |
| followed by four coordinates indicating the four row-indices. So, this mode | |
| takes a total of 5 coordinates as input arguments. For more information on | |
| `gather4` mode, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-tiled-scatter4-gather4-modes). | |
| - The last two operands are a boolean cache-hint flag and the data-validity | |
| reporting pattern. They must be compile-time constants. When `i1 %flag_ch` | |
| is set, it indicates a valid cache hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| - The last `i32 %flag_valid_pattern` flag selects the data-validity reporting | |
| mechanism; see | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.3d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.3d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.128.3d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.128.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.128.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.[3-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.tensor.[1-5]d.shared::cta.global.*` set of | |
| PTX instructions. These instructions initiate an asynchronous copy of tensor | |
| data from global memory to shared::cta memory in `im2col` mode. In im2col | |
| mode, some dimensions of the source tensor are unrolled into a single | |
| dimensional column at the destination. In this mode, the tensor has to be at | |
| least three-dimensional. Along with the tensor coordinates, im2col offsets are | |
| also specified (denoted by `i16 im2col0...i16 %im2col2`). For the `im2col` | |
| mode, the number of offsets is two less than the number of dimensions of the | |
| tensor operation. For the `im2col.w` and `im2col.w.128` mode, the number of | |
| offsets is always 2, denoted by `i16 %wHalo` and `i16 %wOffset` arguments. | |
| For more information on `im2col.w` and `im2col.w.128` modes, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-im2col-w-w128-modes). | |
| The last two operands are a boolean cache-hint flag and the data-validity | |
| reporting pattern, with the same functionality as described in the `tile` | |
| mode intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.tile*.override*[1-5]d`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.1d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.1d.i32(..., i32 %mc, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.2d.{i16,i32}(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.3d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.gather4.override.addr.2d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.gather4.override.addr.2d.i32(..., i32 %mc, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.dim.1d.i32(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i32 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.dim.stride.2d.i32(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i32 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.dim.stride.3d.i32(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.dim.stride.4d.i32(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.tile.override.addr.dim.stride.5d.i32(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously copy tile-mode tensor data from Global memory to Shared Cluster memory | |
| while allowing overriding properties in the opaque tensor map with | |
| explicit operands. The override operands and their restrictions are described | |
| in [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| - `%flag_mc` controls whether the multicast mask (i16/i32 `%mc`) is emitted. | |
| - `%flag_ch` controls whether `%ch` cache hint is emitted. | |
| - `%cta_group` selects CTA-group qualifier, it can represent three values: | |
| | Value | CTA Group Modifier | | |
| |-------|---------------------------------| | |
| | 0 | no cta group modifier (default) | | |
| | 1 | `cta_group::1` | | |
| | 2 | `cta_group::2` | | |
| - The last `i32 %flag_valid_pattern` flag is described in | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| All above described flag operands must be compile time constants. | |
| These intrinsics lower to the `.override::global_address` form, optionally | |
| with `.override::global_dim` or `.override::global_dim_stride`, and require | |
| `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.im2col*.override.addr.[3-5]d`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.override.addr.3d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.override.addr.3d.i32(..., i32 %mc, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.override.addr.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.override.addr.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.override.addr.3d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.override.addr.3d.i32(..., i32 %mc, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.override.addr.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.override.addr.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.override.addr.3d.i16(ptr addrspace(7) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i16 %mc, i64 %ch, i1 %flag_mc, i1 %flag_ch, i32 %cta_group, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.override.addr.3d.i32(..., i32 %mc, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.override.addr.4d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.im2col.w.128.override.addr.5d.{i16,i32}(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously copy im2col-mode tensor data from Global memory to Shared Cluster memory | |
| while allowing overriding properties in the opaque tensor map with | |
| explicit operands. The override operands and their restrictions are described | |
| in [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| The `%flag_mc`, `%flag_ch`, `%cta_group`, and `%flag_valid_pattern` operands | |
| have the same functionality as described in the `tile` mode override | |
| intrinsics above. All these flag operands must be compile-time constants. | |
| These intrinsics lower to the `.override::global_address` form, optionally | |
| with `.override::global_dim` or `.override::global_dim_stride`, and require | |
| `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile*.override*[1-5]d`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.1d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.gather4.override.addr.2d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.dim.1d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.dim.stride.2d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.dim.stride.3d(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.dim.stride.4d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.tile.override.addr.dim.stride.5d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously copy tile-mode tensor data from Global memory to Shared CTA memory | |
| while allowing overriding properties in the opaque tensor map with | |
| explicit operands. The override operands and their restrictions are described | |
| in [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| - `%flag_ch` controls whether `%ch` cache hint is emitted. | |
| - The last `i32 %flag_valid_pattern` flag is described in | |
| [TMA Global-to-Shared Data-Validity Reporting](#tma-global-to-shared-data-validity-reporting). | |
| All above described flag operands must be compile time constants. | |
| These intrinsics lower to the `.override::global_address` form, optionally | |
| with `.override::global_dim` or `.override::global_dim_stride`, and require | |
| `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col*.override.addr.[3-5]d`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.override.addr.3d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.override.addr.3d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.128.override.addr.3d(ptr addrspace(3) %dst, ptr addrspace(3) %bar, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i64 %ch, i1 %flag_ch, i32 %flag_valid_pattern) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.128.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.g2s.cta.im2col.w.128.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| These intrinsics asynchronously copy im2col-mode tensor data from Global memory to Shared CTA memory | |
| while allowing overriding properties in the opaque tensor map with | |
| explicit operands. The override operands and their restrictions are described | |
| in [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| The `%flag_ch` and `%flag_valid_pattern` operands have the same functionality | |
| as described in the `tile` mode override intrinsics above. All these flag | |
| operands must be compile-time constants. | |
| These intrinsics lower to the `.override::global_address` form, optionally | |
| with `.override::global_dim` or `.override::global_dim_stride`, and require | |
| `sm_107f` and PTX 9.4 or later. | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.s2g.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.1d(ptr addrspace(3) %src, ptr %tensor_map, i32 %d0, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.scatter4.2d(ptr addrspace(3) %src, ptr %tensor_map, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i64 %ch, i1 %flag_ch) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.s2g.tile.[1-5]d`' intrinsics correspond | |
| to the `cp.async.bulk.tensor.[1-5]d.*` set of PTX instructions. These | |
| instructions initiate an asynchronous copy of tensor data from shared::cta to | |
| global memory (indicated by the `s2g` prefix) in `tile` mode. The dimension | |
| of the tensor data ranges from 1d to 5d with the coordinates specified by the | |
| `i32 %d0 ... i32 %d4` arguments. In `tile.scatter4` mode, a single 2D source | |
| tensor is divided into four rows in the 2D destination tensor. The first | |
| coordinate `i32 %x0` denotes the column index followed by four coordinates | |
| indicating the four row-indices. So, this mode takes a total of 5 coordinates as | |
| input arguments. For more information on `scatter4` mode, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-tiled-scatter4-gather4-modes). | |
| - The last argument to these intrinsics is a boolean flag indicating support for | |
| cache_hint. This flag argument must be a compile-time constant. When set, it | |
| indicates a valid cache_hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.3d(ptr addrspace(3) %src, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.[3-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.tensor.im2col_no_offs.[3-5]d.*` set of PTX instructions. | |
| These instructions initiate an asynchronous copy of tensor data from | |
| shared::cta to global memory (indicated by the `s2g` prefix) in `im2col` | |
| mode. In this mode, the tensor has to be at least three-dimensional. Unlike the | |
| `g2s` variants, there are no im2col_offsets for these intrinsics. The last | |
| argument to these intrinsics is a boolean flag, with the same functionality as | |
| described in the `s2g.tile` mode intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.3d(ptr addrspace(3) %src, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.[3-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.tensor.im2col_no_offs::w.[3-5]d.*` set of PTX instructions. | |
| These instructions initiate an asynchronous copy of tensor data from | |
| shared::cta to global memory (indicated by the `s2g` prefix) in `im2col_w` | |
| mode. In this mode, the tensor has to be at least three-dimensional. Unlike the | |
| `g2s` variants, there are no im2col_offsets for these intrinsics. The last | |
| argument to these intrinsics is a boolean flag, with the same functionality as | |
| described in the `s2g.tile` mode intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.reduce.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.1d(ptr addrspace(3) %src, ptr %tensor_map, i32 %d0, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.reduce.tile.[1-5]d`' intrinsics | |
| correspond to the `cp.reduce.async.bulk.tensor.[1-5]d.global.shared::cta.*` | |
| set of PTX instructions. These instructions initiate an asynchronous reduction | |
| operation of tensor data in global memory with the tensor data in shared::cta | |
| memory, using `tile` mode. The dimension of the tensor data ranges from 1d to | |
| 5d with the coordinates specified by the `i32 %d0 ... i32 %d4` arguments. The | |
| `i32 %red_op` argument selects the reduction operation to perform. It must be | |
| a compile-time constant in the half-open range `[0, 8)`, with the following | |
| encoding: | |
| | `red_op` | Reduction Operation | | |
| |:--------:|:--------------------| | |
| | 0 | `add` | | |
| | 1 | `min` | | |
| | 2 | `max` | | |
| | 3 | `inc` | | |
| | 4 | `dec` | | |
| | 5 | `and` | | |
| | 6 | `or` | | |
| | 7 | `xor` | | |
| The symbolic LLVM IR annotation for `red_op` and the PTX reduction suffix use | |
| the same canonical operator spelling. | |
| - The last argument to these intrinsics is a boolean flag indicating support | |
| for cache_hint. This flag argument must be a compile-time constant. When | |
| set, it indicates a valid cache_hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-reduce-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.3d(ptr addrspace(3) %src, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.[3-5]d`' intrinsics | |
| correspond to the `cp.reduce.async.bulk.tensor.im2col_no_offs.[3-5]d.global.shared::cta.*` | |
| set of PTX instructions. These instructions initiate an asynchronous reduction | |
| operation of tensor data in global memory with the tensor data in shared::cta | |
| memory, using `im2col` mode. In this mode, the tensor has to be at least | |
| three-dimensional. The supported reduction operations are the same as the ones | |
| in the `tile` mode. The `i32 %red_op` argument and the last boolean flag | |
| argument have the same functionality as described in the `tile` mode | |
| intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-reduce-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.3d(ptr addrspace(3) %src, ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.[3-5]d`' | |
| intrinsics correspond to the `cp.reduce.async.bulk.tensor.im2col_no_offs::w.[3-5]d.*` set of PTX | |
| instructions. These instructions initiate an asynchronous reduction operation of | |
| tensor data in global memory with the tensor data in shared\{::cta} memory, using | |
| `im2col_w` mode. In this mode, the tensor has to be at least three-dimensional. | |
| The supported reduction operations are the same as the ones | |
| in the `tile` mode. The `i32 %red_op` argument and the last boolean flag | |
| argument have the same functionality as described in the `tile` mode | |
| intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-reduce-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.1d(ptr %tensor_map, i32 %d0, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.gather4.2d(ptr %tensor_map, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i64 %ch, i1 %flag_ch) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.prefetch.tile.[1-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.prefetch.tensor.[1-5]d.L2.global*` set of | |
| PTX instructions. These instructions initiate an asynchronous prefetch of tensor | |
| data from global memory to the L2 cache. In tile mode, the multi-dimensional | |
| layout of the source tensor is preserved at the destination. The dimension of | |
| the tensor data ranges from 1d to 5d with the coordinates specified by the | |
| `i32 %d0 ... i32 %d4` arguments. | |
| In `tile.gather4` mode, four rows in the 2-dimnesional source tensor are | |
| fetched to the L2 cache. The first coordinate `i32 %x0` denotes the column | |
| index followed by four coordinates indicating the four row-indices. So, this | |
| mode takes a total of 5 coordinates as input arguments. For more information | |
| on `gather4` mode, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-tiled-scatter4-gather4-modes). | |
| - The last argument to these intrinsics is a boolean flag indicating support | |
| for cache_hint. This flag argument must be a compile-time constant. When set, | |
| it indicates a valid cache_hint (`i64 %ch`) and generates the | |
| `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %im2col0, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i16 %im2col0, i16 %im2col1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, i16 %im2col0, i16 %im2col1, i16 %im2col2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.128.3d(ptr %tensor_map, i32 %d0, i32 %d1, i32 %d2, i16 %wHalo, i16 %wOffset, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.128.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.w.128.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.prefetch.im2col.[3-5]d`' intrinsics | |
| correspond to the `cp.async.bulk.prefetch.tensor.[1-5]d.L2.global*` set of PTX | |
| instructions. These instructions initiate an asynchronous prefetch of tensor | |
| data from global memory to the L2 cache. In im2col mode, some dimensions of the | |
| source tensor are unrolled into a single dimensional column at the destination. | |
| In this mode, the tensor has to be at least three-dimensional. Along with the | |
| tensor coordinates, im2col offsets are also specified (denoted by | |
| `i16 im2col0...i16 %im2col2`). For `im2col` mode, the number of offsets is | |
| two less than the number of dimensions of the tensor operation. For the | |
| `im2col.w` and `im2col.w.128` modes, the number of offsets is always 2, | |
| denoted by `i16 %wHalo` and `i16 %wOffset` arguments. For more information | |
| on `im2col.w` and `im2col.w.128` modes, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-im2col-w-w128-modes). | |
| The last argument to these intrinsics is a boolean flag, with the same | |
| functionality as described in the `tile` mode intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-cp-async-bulk-prefetch-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override*.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| ; override.addr | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.1d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ; override.addr.dim (1D only) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.dim.1d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i64 %ch, i1 %flag_ch) | |
| ; override.addr.dim.stride (2D and higher) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.dim.stride.2d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.dim.stride.3d(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.dim.stride.4d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override.addr.dim.stride.5d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ; scatter4 + override.addr (2D only) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.tile.scatter4.override.addr.2d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %x0, i32 %y0, i32 %y1, i32 %y2, i32 %y3, i64 %ch, i1 %flag_ch) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.s2g.tile.override*.[1-5]d`' intrinsics correspond to the `cp.async.bulk.tensor.[1-5]d.*` set of PTX instructions qualified with the `.override::*` qualifiers. | |
| These instructions initiate an asynchronous copy of tensor data from shared::cta to global memory (indicated by the `s2g` prefix) in `tile` mode, while overriding specific properties of the opaque tensor-map object with explicit instruction operands. | |
| The `.override::*` qualifiers ignore certain tensor-map properties and | |
| instead take them as explicit instruction operands. | |
| The supported override variants, operands, and restrictions are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| The `scatter4.override.addr` variant combines the `tile.scatter4` mode (2D only) with the base address override. | |
| In `tile.scatter4` mode, a single 2D source tensor is divided into four rows in the 2D destination tensor. | |
| The first coordinate `i32 %x0` denotes the column index followed by four coordinates indicating the four row-indices. | |
| For more information on `scatter4` mode, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-tiled-scatter4-gather4-modes). | |
| - The last argument to these intrinsics is a boolean flag indicating support for cache_hint. | |
| This flag argument must be a compile-time constant. | |
| When set, it indicates a valid cache_hint (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.override.addr.3d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.override.addr.[3-5]d`' intrinsics correspond to the `cp.async.bulk.tensor.im2col_no_offs.[3-5]d.*` set of PTX instructions qualified with the `.override::global_address` qualifier. | |
| They copy tensor data from shared::cta to global memory in `im2col` mode | |
| using the `override.addr` operands described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| The last boolean flag argument has the same functionality as described in the | |
| `s2g.tile` override intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.override.addr.3d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.s2g.im2col.w.override.addr.[3-5]d`' intrinsics correspond to the `cp.async.bulk.tensor.im2col_no_offs::w.[3-5]d.*` set of PTX instructions qualified with the `.override::global_address` qualifier. | |
| They copy tensor data from shared::cta to global memory in `im2col_w` mode | |
| using the `override.addr` operands described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| The last boolean flag argument has the same functionality as described in the | |
| `s2g.tile` override intrinsics above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override*.[1-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| ; override.addr | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.1d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.2d(..., i32 %d0, i32 %d1, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.3d(..., i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ; override.addr.dim (1D only) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.dim.1d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i32 %d0, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| ; override.addr.dim.stride (2D and higher) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.dim.stride.2d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i16 %ts0, i16 %ts1, i32 %stride0, i16 %upper_stride, i32 %d0, i32 %d1, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.dim.stride.3d(..., i16 %ts0, i16 %ts1, i16 %ts2, i32 %stride0, i32 %stride1, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.dim.stride.4d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i32 %stride0, i32 %stride1, i32 %stride2, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override.addr.dim.stride.5d(..., i16 %ts0, i16 %ts1, i16 %ts2, i16 %ts3, i16 %ts4, i32 %stride0, i32 %stride1, i32 %stride2, i32 %stride3, i16 %upper_stride, i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.reduce.tile.override*.[1-5]d`' intrinsics correspond to the `cp.reduce.async.bulk.tensor.[1-5]d.global.shared::cta.*` set of PTX instructions qualified with the `.override::*` qualifiers. | |
| These instructions initiate an asynchronous reduction operation of tensor data in global memory with the tensor data in shared::cta memory, using `tile` mode, while overriding specific properties of the opaque tensor-map object with explicit instruction operands. | |
| The supported override variants and operands are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| The `i32 %red_op` argument selects the reduction operation to perform. | |
| It must be a compile-time constant in the half-open range `[0, 8)`, with the following encoding: | |
| | `red_op` | Reduction Operation | | |
| |:--------:|:--------------------| | |
| | 0 | `add` | | |
| | 1 | `min` | | |
| | 2 | `max` | | |
| | 3 | `inc` | | |
| | 4 | `dec` | | |
| | 5 | `and` | | |
| | 6 | `or` | | |
| | 7 | `xor` | | |
| The symbolic LLVM IR annotation for `red_op` and the PTX reduction suffix use the same canonical operator spelling. | |
| - The last argument to these intrinsics is a boolean flag indicating support for cache_hint. | |
| This flag argument must be a compile-time constant. | |
| When set, it indicates a valid cache_hint (`i64 %ch`) and generates the `.L2::cache_hint` variant of the PTX instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-reduce-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.override.addr.3d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.override.addr.[3-5]d`' intrinsics correspond to the `cp.reduce.async.bulk.tensor.im2col_no_offs.[3-5]d.global.shared::cta.*` set of PTX instructions qualified with the `.override::global_address` qualifier. | |
| These instructions initiate an asynchronous reduction operation of tensor data in global memory with the tensor data in shared::cta memory, using `im2col` mode, while overriding the global base address from the tensor-map with the explicit `ptr addrspace(1) %override_addr` operand. | |
| The `override.addr` operands are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| `%red_op` and the last boolean flag are as described for `reduce.tile` above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-reduce-async-bulk-tensor). | |
| #### '`llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.override.addr.[3-5]d`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.override.addr.3d(ptr addrspace(3) %src, ptr %tensor_map, ptr addrspace(1) %override_addr, i32 %d0, i32 %d1, i32 %d2, i64 %ch, i32 %red_op, i1 %flag_ch) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.override.addr.4d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, ...) | |
| declare void @llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.override.addr.5d(..., i32 %d0, i32 %d1, i32 %d2, i32 %d3, i32 %d4, ...) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.cp.async.bulk.tensor.reduce.im2col.w.override.addr.[3-5]d`' intrinsics correspond to the `cp.reduce.async.bulk.tensor.im2col_no_offs::w.[3-5]d.global.shared::cta.*` set of PTX instructions qualified with the `.override::global_address` qualifier. | |
| These instructions initiate an asynchronous reduction operation of tensor data in global memory with the tensor data in shared::cta memory, using `im2col_w` mode, while overriding the global base address from the tensor-map with the explicit `ptr addrspace(1) %override_addr` operand. | |
| The `override.addr` operands are described in | |
| [Tensor-Map Property Overrides](#tensor-map-property-overrides). | |
| `%red_op` and the last boolean flag are as described for `reduce.tile` above. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#data-movement-and-conversion-instructions-cp-reduce-async-bulk-tensor). | |
| ### Warp Group Intrinsics | |
| #### '`llvm.nvvm.wgmma.fence.sync.aligned`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.wgmma.fence.sync.aligned() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.wgmma.fence.sync.aligned`' intrinsic generates the | |
| `wgmma.fence.sync.aligned` PTX instruction, which establishes an ordering | |
| between prior accesses to any warpgroup registers and subsequent accesses to | |
| the same registers by a `wgmma.mma_async` instruction. | |
| The `wgmma.fence` instruction must be issued by all warps of the warpgroup in | |
| the following locations: | |
| - Before the first `wgmma.mma_async` operation in a warpgroup. | |
| - Between a register access by a thread in the warpgroup and any | |
| `wgmma.mma_async` instruction that accesses the same registers, except when | |
| these are accumulator register accesses across multiple `wgmma.mma_async` | |
| instructions of the same shape in which case an ordering guarantee is | |
| provided by default. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#asynchronous-warpgroup-level-matrix-instructions-wgmma-fence). | |
| #### '`llvm.nvvm.wgmma.commit_group.sync.aligned`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.wgmma.commit_group.sync.aligned() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.wgmma.commit_group.sync.aligned`' intrinsic generates the | |
| `wgmma.commit_group.sync.aligned` PTX instruction, which creates a new | |
| wgmma-group per warpgroup and batches all prior `wgmma.mma_async` | |
| instructions initiated by the executing warp but not committed to any | |
| wgmma-group into the new wgmma-group. If there are no uncommitted `wgmma | |
| mma_async` instructions then, `wgmma.commit_group` results in an empty | |
| wgmma-group. | |
| An executing thread can wait for the completion of all `wgmma.mma_async` | |
| operations in a wgmma-group by using `wgmma.wait_group`. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#asynchronous-warpgroup-level-matrix-instructions-wgmma-commit-group). | |
| #### '`llvm.nvvm.wgmma.wait_group.sync.aligned`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.wgmma.wait_group.sync.aligned(i64 immarg N) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.wgmma.wait_group.sync.aligned`' intrinsic generates the | |
| `wgmma.commit_group.sync.aligned N` PTX instruction, which will cause the | |
| executing thread to wait until only `N` or fewer of the most recent | |
| wgmma-groups are pending and all the prior wgmma-groups committed by the | |
| executing threads are complete. For example, when `N` is 0, the executing | |
| thread waits on all the prior wgmma-groups to complete. Operand `N` is an | |
| integer constant. | |
| Accessing the accumulator register or the input register containing the | |
| fragments of matrix A of a `wgmma.mma_async` instruction without first | |
| performing a `wgmma.wait_group` instruction that waits on a wgmma-group | |
| including that `wgmma.mma_async` instruction is undefined behavior. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#asynchronous-warpgroup-level-matrix-instructions-wgmma-wait-group). | |
| #### '`llvm.nvvm.griddepcontrol.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.griddepcontrol.launch_dependents() | |
| declare void @llvm.nvvm.griddepcontrol.wait() | |
| ``` | |
| ##### Overview: | |
| The `griddepcontrol` intrinsics allows the dependent grids and prerequisite | |
| grids as defined by the runtime, to control execution in the following way: | |
| `griddepcontrol.launch_dependents` intrinsic signals that the dependents can | |
| be scheduled, before the current grid completes. The intrinsic can be invoked by | |
| multiple threads in the current CTA and repeated invocations of the intrinsic | |
| will have no additional side effects past that of the first invocation. | |
| `griddepcontrol.wait` intrinsic causes the executing thread to wait until all | |
| prerequisite grids in flight have completed and all the memory operations from | |
| the prerequisite grids are performed and made visible to the current grid. | |
| For more information, refer | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#parallel-synchronization-and-communication-instructions-griddepcontrol). | |
| ### Tensormap Replace Intrinsics | |
| These intrinsics modify the fields of the tensor-map object at `%addr` in | |
| `tile` mode. | |
| For more information, refer to the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-tensormap-replace). | |
| #### '`llvm.nvvm.tensormap.replace.global.address`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.global.address.p1(ptr addrspace(1) %addr, i64 %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.global.address.p3(ptr addrspace(3) %addr, i64 %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.global.address.*`' intrinsics replace the | |
| `global_address` field of the tensor-map object with `%new_value`. | |
| #### '`llvm.nvvm.tensormap.replace.rank`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.rank.p1(ptr addrspace(1) %addr, i32 %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.rank.p3(ptr addrspace(3) %addr, i32 %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.rank.*`' intrinsics replace the `rank` | |
| field of the tensor-map object with `%new_value` which must be one less than | |
| the desired tensor rank as this field uses zero-based numbering. | |
| #### '`llvm.nvvm.tensormap.replace.global.stride`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.global.stride.p1(ptr addrspace(1) %addr, i32 immarg %ord, i64 %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.global.stride.p3(ptr addrspace(3) %addr, i32 immarg %ord, i64 %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.global.stride.*`' intrinsics replace the | |
| `%ord`-th element of the `global_stride` field of the tensor-map object | |
| with `%new_value`. `%ord` must be in the range [0, 4]. | |
| #### '`llvm.nvvm.tensormap.replace.element.stride`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.element.stride.p1(ptr addrspace(1) %addr, i32 immarg %ord, i32 %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.element.stride.p3(ptr addrspace(3) %addr, i32 immarg %ord, i32 %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.element.stride.*`' intrinsics replace the | |
| `%ord`-th element of the `element_stride` field of the tensor-map object | |
| with `%new_value`. `%ord` must be in the range [0, 4]. | |
| #### '`llvm.nvvm.tensormap.replace.global.dim`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.global.dim.p1(ptr addrspace(1) %addr, i32 immarg %ord, i32 %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.global.dim.p3(ptr addrspace(3) %addr, i32 immarg %ord, i32 %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.global.dim.*`' intrinsics replace the | |
| `%ord`-th element of the `global_dim` field of the tensor-map object | |
| with `%new_value`. `%ord` must be in the range [0, 4]. | |
| #### '`llvm.nvvm.tensormap.replace.box.dim`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.box.dim.p1(ptr addrspace(1) %addr, i32 immarg %ord, i32 %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.box.dim.p3(ptr addrspace(3) %addr, i32 immarg %ord, i32 %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.box.dim.*`' intrinsics replace the | |
| `%ord`-th element of the `box_dim` field of the tensor-map object with | |
| `%new_value`. `%ord` must be in the range [0, 4]. | |
| #### '`llvm.nvvm.tensormap.replace.elemtype`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.elemtype.p1(ptr addrspace(1) %addr, i32 immarg %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.elemtype.p3(ptr addrspace(3) %addr, i32 immarg %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.elemtype.*`' intrinsics replace the | |
| `elemtype` field of the tensor-map object with the type specified by | |
| `%new_value`. | |
| ##### Semantics: | |
| The following table shows the mapping of `%new_value` to the actual element | |
| type: | |
| | Element Type | Value | | |
| | ------------------------ | ----- | | |
| | `u8` | 0 | | |
| | `u16` | 1 | | |
| | `u32` | 2 | | |
| | `s32` | 3 | | |
| | `u64` | 4 | | |
| | `s64` | 5 | | |
| | `f16` | 6 | | |
| | `f32` | 7 | | |
| | `f32.ftz` | 8 | | |
| | `f64` | 9 | | |
| | `bf16` | 10 | | |
| | `tf32` | 11 | | |
| | `tf32.ftz` | 12 | | |
| | `b4x16` | 13 | | |
| | `b4x16_p64` | 14 | | |
| | `b6x16_p32` or `b6p2x16` | 15 | | |
| #### '`llvm.nvvm.tensormap.replace.interleave.layout`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.interleave.layout.p1(ptr addrspace(1) %addr, i32 immarg %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.interleave.layout.p3(ptr addrspace(3) %addr, i32 immarg %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.interleave.layout.*`' intrinsics replace | |
| the `interleave_layout` field of the tensor-map object with the layout | |
| specified by `%new_value`. | |
| ##### Semantics: | |
| The following table shows the mapping of `%new_value` to the actual layout: | |
| | Interleave Layout | Value | | |
| | ----------------- | ----- | | |
| | `No interleave` | 0 | | |
| | `16B interleave` | 1 | | |
| | `32B interleave` | 2 | | |
| #### '`llvm.nvvm.tensormap.replace.swizzle_mode`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.swizzle.mode.p1(ptr addrspace(1) %addr, i32 immarg %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.swizzle.mode.p3(ptr addrspace(3) %addr, i32 immarg %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.swizzle.mode.*`' intrinsics replace the | |
| `swizzle_mode` field of the tensor-map object with the swizzle mode specified | |
| by `%new_value`. | |
| ##### Semantics: | |
| The following table shows the mapping of `%new_value` to the actual swizzle | |
| mode: | |
| | Swizzle Mode | Value | | |
| | -------------- | ----- | | |
| | `No swizzle` | 0 | | |
| | `32B swizzle` | 1 | | |
| | `64B swizzle` | 2 | | |
| | `128B swizzle` | 3 | | |
| | `96B swizzle` | 4 | | |
| #### '`llvm.nvvm.tensormap.replace.swizzle_atomicity`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.swizzle.atomicity.p1(ptr addrspace(1) %addr, i32 immarg %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.swizzle.atomicity.p3(ptr addrspace(3) %addr, i32 immarg %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.swizzle.atomicity.*`' intrinsics replace | |
| the `swizzle_atomicity` field of the tensor-map object with the swizzle | |
| atomicity specified by `%new_value`. | |
| ##### Semantics: | |
| The following table shows the mapping of `%new_value` to the actual swizzle | |
| atomicity: | |
| | Swizzle Atomicity | Value | | |
| | ----------------- | ----- | | |
| | `16B` | 0 | | |
| | `32B` | 1 | | |
| | `32B + 8B flip` | 2 | | |
| | `64B` | 3 | | |
| #### '`llvm.nvvm.tensormap.replace.fill_mode`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tensormap.replace.fill.mode.p1(ptr addrspace(1) %addr, i32 immarg %new_value) | |
| declare void @llvm.nvvm.tensormap.replace.fill.mode.p3(ptr addrspace(3) %addr, i32 immarg %new_value) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tensormap.replace.fill.mode.*`' intrinsics replace the | |
| `fill_mode` field of the tensor-map object with the fill mode specified by | |
| `%new_value`. | |
| ##### Semantics: | |
| The following table shows the mapping of `%new_value` to the actual fill mode: | |
| | Fill Mode | Value | | |
| | -------------- | ----- | | |
| | `Zero fill` | 0 | | |
| | `OOB-NaN fill` | 1 | | |
| ### TCGEN05 family of Intrinsics | |
| The llvm.nvvm.tcgen05.\* intrinsics model the TCGEN05 family of instructions | |
| exposed by PTX. These intrinsics use 'Tensor Memory' (henceforth `tmem`). | |
| NVPTX represents this memory using `addrspace(6)` and is always 32-bits. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-memory). | |
| The tensor-memory pointers may only be used with the tcgen05 intrinsics. | |
| There are specialized load/store instructions provided (tcgen05.ld/st) to | |
| work with tensor-memory. | |
| For more information on tensor-memory load/store instructions, refer to [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-memory-and-register-load-store-instructions). | |
| #### '`llvm.nvvm.tcgen05.alloc`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.alloc.{cg1,cg2}.p0(ptr %dst, i32 %ncols, i1 %is_exclusive_flag) | |
| declare void @llvm.nvvm.tcgen05.alloc.{cg1,cg2}.p3(ptr addrspace(3) %dst, i32 %ncols, i1 %is_exclusive_flag) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.alloc.*`' intrinsics correspond to the | |
| `tcgen05.alloc{.exclusive}.cta_group*.sync.aligned.b32` family of PTX | |
| instructions. The `tcgen05.alloc` is a potentially blocking instruction which | |
| dynamically allocates the specified number of columns in the Tensor Memory | |
| and writes the address of the allocated Tensor Memory into shared memory at the | |
| location specified by `%dst`. The 32-bit operand `%ncols` specifies the number | |
| of columns to be allocated and it must be a power-of-two for non-exclusive | |
| allocations and it must be a multiple of 32 for exclusive allocations. The | |
| overloaded pointer argument may use generic or shared memory address space; | |
| a shared pointer emits the `.shared::cta` qualifier. The `.cg1` and `.cg2` | |
| variants generate `cta_group::1` and `cta_group::2` variants of the instruction | |
| respectively. The last argument to these intrinsics is a boolean flag indicating | |
| support for exclusive allocation. This flag argument must be a compile-time | |
| constant. When set, the `.exclusive` variant is emitted, which claims ownership | |
| of the allocation permit. No other allocation may exist at the same time as an | |
| exclusive allocation. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-memory-allocation-and-management-instructions). | |
| #### '`llvm.nvvm.tcgen05.dealloc`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.dealloc.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i32 %ncols, i1 %is_exclusive_flag) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.dealloc.*`' intrinsics correspond to the | |
| `tcgen05.dealloc{.exclusive}.*` set of PTX instructions. The `tcgen05.dealloc` | |
| instructions deallocates the Tensor Memory specified by the Tensor Memory | |
| address `%tmem_addr`. The operand `%tmem_addr` must point to a previous | |
| Tensor Memory allocation. The 32-bit operand `%ncols` specifies the number | |
| of columns to be de-allocated. The `.cg1` and `.cg2` variants generate | |
| `cta_group::1` and `cta_group::2` variants of the instruction respectively. | |
| The last argument to these intrinsics is a boolean flag indicating | |
| support for exclusive allocation. This flag argument must be a compile-time | |
| constant. When set, the `.exclusive` variant is emitted. Memory must | |
| be deallocated with `.exclusive` if and only if it was allocated with | |
| `.exclusive`. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-memory-allocation-and-management-instructions). | |
| #### '`llvm.nvvm.tcgen05.relinq.alloc.permit`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.relinq.alloc.permit.cg1() | |
| declare void @llvm.nvvm.tcgen05.relinq.alloc.permit.cg2() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.relinq.alloc.permit.*`' intrinsics correspond to the | |
| `tcgen05.relinquish_alloc_permit.*` set of PTX instructions. This instruction | |
| specifies that the CTA of the executing thread is relinquishing the right to | |
| allocate Tensor Memory. So, it is illegal for a CTA to perform `tcgen05.alloc` | |
| after any of its constituent threads execute | |
| `tcgen05.relinquish_alloc_permit`. The `.cg1` and `.cg2` variants generate | |
| `cta_group::1` and `cta_group::2` flavors of the instruction respectively. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensor-memory-allocation-and-management-instructions). | |
| #### '`llvm.nvvm.tcgen05.commit`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.commit.{cg1,cg2}.p0(ptr %mbar) | |
| declare void @llvm.nvvm.tcgen05.commit.{cg1,cg2}.p3(ptr addrspace(3) %mbar) | |
| declare void @llvm.nvvm.tcgen05.commit.mc.{cg1,cg2}.p0.i16(ptr %mbar, i16 %mc) | |
| declare void @llvm.nvvm.tcgen05.commit.mc.{cg1,cg2}.p0.i32(ptr %mbar, i32 %mc) | |
| declare void @llvm.nvvm.tcgen05.commit.mc.{cg1,cg2}.p3.i16(ptr addrspace(3) %mbar, i16 %mc) | |
| declare void @llvm.nvvm.tcgen05.commit.mc.{cg1,cg2}.p3.i32(ptr addrspace(3) %mbar, i32 %mc) | |
| declare void @llvm.nvvm.tcgen05.commit.smem.a.read.{cg1,cg2}.p0(ptr %bar_addr) | |
| declare void @llvm.nvvm.tcgen05.commit.smem.a.read.{cg1,cg2}.p3(ptr addrspace(3) %bar_addr) | |
| declare void @llvm.nvvm.tcgen05.commit.smem.a.read.mc.{cg1,cg2}.p0.i16(ptr %bar_addr, i16 %cta_mask) | |
| declare void @llvm.nvvm.tcgen05.commit.smem.a.read.mc.{cg1,cg2}.p0.i32(ptr %bar_addr, i32 %cta_mask) | |
| declare void @llvm.nvvm.tcgen05.commit.smem.a.read.mc.{cg1,cg2}.p3.i16(ptr addrspace(3) %bar_addr, i16 %cta_mask) | |
| declare void @llvm.nvvm.tcgen05.commit.smem.a.read.mc.{cg1,cg2}.p3.i32(ptr addrspace(3) %bar_addr, i32 %cta_mask) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.commit.*`' intrinsics correspond to the | |
| `tcgen05.commit.{cg1/cg2}.mbarrier::arrive::one.*` set of PTX instructions. | |
| The `tcgen05.commit` is an asynchronous instruction which makes the mbarrier | |
| object (`%mbar`) track the completion of all prior asynchronous tcgen05 | |
| operations. The `.mc` variants allow signaling on the mbarrier objects of | |
| multiple CTAs (specified by `%mc`) in the cluster. The `.cg1` and `.cg2` | |
| variants generate `cta_group::1` and `cta_group::2` flavors of the | |
| instruction, respectively. The `smem.a.read` variants track the completion | |
| of reads of A-matrix from shared memory for all prior `tcgen05.mma` operations. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen-async-sync-operations-commit). | |
| #### '`llvm.nvvm.tcgen05.wait`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.wait.ld() | |
| declare void @llvm.nvvm.tcgen05.wait.st() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.wait.ld/st`' intrinsics correspond to the | |
| `tcgen05.wait::{ld/st}.sync.aligned` pair of PTX instructions. The | |
| `tcgen05.wait::ld` causes the executing thread to block until all prior | |
| `tcgen05.ld` operations issued by the executing thread have completed. The | |
| `tcgen05.wait::st` causes the executing thread to block until all prior | |
| `tcgen05.st` operations issued by the executing thread have completed. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instructions-tcgen05-wait). | |
| #### '`llvm.nvvm.tcgen05.fence`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.fence.before.thread.sync() | |
| declare void @llvm.nvvm.tcgen05.fence.after.thread.sync() | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.fence.*`' intrinsics correspond to the | |
| `tcgen05.fence::{before/after}_thread_sync` pair of PTX instructions. These | |
| instructions act as code motion fences for asynchronous tcgen05 operations. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tensorcore-5th-generation-instructions-tcgen05-fence). | |
| #### '`llvm.nvvm.tcgen05.shift`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.shift.down.cg1(ptr addrspace(6) %tmem_addr) | |
| declare void @llvm.nvvm.tcgen05.shift.down.cg2(ptr addrspace(6) %tmem_addr) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.shift.{cg1/cg2}`' intrinsics correspond to the | |
| `tcgen05.shift.{cg1/cg2}` PTX instructions. The `tcgen05.shift` is an | |
| asynchronous instruction which initiates the shifting of 32-byte elements | |
| downwards across all the rows, except the last, by one row. The address operand | |
| `%tmem_addr` specifies the base address of the matrix in the Tensor Memory | |
| whose rows must be down shifted. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instructions-tcgen05-shift). | |
| #### '`llvm.nvvm.tcgen05.cp`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.cp.4x256b.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.128x256b.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.128x128b.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.32x128b_warpx4.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.64x128b_warpx2_02_13.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.64x128b_warpx2_01_23.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.4x256b.b6x16_p32.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.128x256b.b6x16_p32.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.128x128b.b6x16_p32.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.32x128b_warpx4.b6x16_p32.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.64x128b_warpx2_02_13.b6x16_p32.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.64x128b_warpx2_01_23.b6x16_p32.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.4x256b.b4x16_p64.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.128x256b.b4x16_p64.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.128x128b.b4x16_p64.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.32x128b_warpx4.b4x16_p64.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.64x128b_warpx2_02_13.b4x16_p64.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| declare void @llvm.nvvm.tcgen05.cp.64x128b_warpx2_01_23.b4x16_p64.{cg1,cg2}(ptr addrspace(6) %tmem_addr, i64 %sdesc) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.tcgen05.cp.{shape}.{src_fmt}.{cg1/cg2}`' intrinsics | |
| correspond to the `tcgen05.cp.*` family of PTX instructions. The | |
| `tcgen05.cp` instruction initiates an asynchronous copy operation from shared | |
| memory to the location specified by `%tmem_addr` in Tensor Memory. The 64-bit | |
| register operand `%sdesc` is the matrix descriptor representing the source | |
| matrix in shared memory that needs to be copied. | |
| The valid shapes for the copy operation are: | |
| {128x256b, 4x256b, 128x128b, 64x128b_warpx2_02_13, 64x128b_warpx2_01_23, | |
| 32x128b_warpx4}. | |
| Shapes `64x128b` and `32x128b` require dedicated multicast qualifiers, | |
| which are appended to the corresponding intrinsic names. | |
| Optionally, the data can be decompressed from the source format in the shared | |
| memory to the destination format in Tensor Memory during the copy operation. | |
| Currently, only `.b8x16` is supported as destination format. The valid source | |
| formats are `.b6x16_p32` and `.b4x16_p64`. | |
| When the source format is `.b6x16_p32`, a contiguous set of 16 elements of | |
| 6-bits each followed by four bytes of padding (`_p32`) in shared memory is | |
| decompressed into 16 elements of 8-bits (`.b8x16`) each in the Tensor Memory. | |
| When the source format is `.b4x16_p64`, a contiguous set of 16 elements of | |
| 4-bits each followed by eight bytes of padding (`_p64`) in shared memory is | |
| decompressed into 16 elements of 8-bits (`.b8x16`) each in the Tensor Memory. | |
| For more information on the decompression schemes, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#optional-decompression). | |
| For more information on the tcgen05.cp instruction, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instructions-tcgen05-cp). | |
| #### '`llvm.nvvm.tcgen05.ld.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare <n x i32> @llvm.nvvm.tcgen05.ld.<shape>.<num>(ptr addrspace(6) %tmem_addr, i1 %pack) | |
| declare <n x i32> @llvm.nvvm.tcgen05.ld.red.32x32b.<num>.i32(ptr addrspace(6) %tmem_addr, i32 %redOp) | |
| declare <n x i32> @llvm.nvvm.tcgen05.ld.red.32x32b.<num>.f32(ptr addrspace(6) %tmem_addr, i32 %redOp, i1 %abs, i1 %nan) | |
| declare <n x i32> @llvm.nvvm.tcgen05.ld.16x32bx2.<num>(ptr addrspace(6) %tmem_addr, i64 %offset, i1 %pack) | |
| declare <n x i32> @llvm.nvvm.tcgen05.ld.red.16x32bx2.<num>.i32(ptr addrspace(6) %tmem_addr, i64 %offset, i32 %redOp) | |
| declare <n x i32> @llvm.nvvm.tcgen05.ld.red.16x32bx2.<num>.f32(ptr addrspace(6) %tmem_addr, i64 %offset, i32 %redOp, i1 %abs, i1 %nan) | |
| ``` | |
| ##### Overview: | |
| This group of intrinsics asynchronously load data from the Tensor Memory at the | |
| ocation specified by the 32-bit address operand `tmem_addr` into the destination | |
| registers, collectively across all threads of the warps. | |
| All the threads in the warp must specify the same value of `tmem_addr`, which | |
| must be the base address of the collective load operation. Otherwise, the | |
| behavior is undefined. | |
| The `shape` qualifier and the `num` qualifier together determines the total | |
| dimension of the data ('n') which is loaded from the Tensor Memory. The `shape` | |
| qualifier indicates the base dimension of data. The `num` qualifier indicates | |
| the repeat factor on the base dimension resulting in the total dimension of the | |
| data that is accessed. | |
| Allowed values for the `num` are `x1, x2, x4, x8, x16, x32, x64, x128` except | |
| for `tcgen05.ld.red` which does not support `x1` | |
| Allowed values for the 'shape' in the first intrinsic are | |
| `16x64b, 16x128b, 16x256b, 32x32b`. | |
| Allowed value for the 'shape' in the second intrinsic is `16x32bx2`. | |
| The result of the intrinsic is a vector consisting of one or more 32-bit | |
| registers derived from `shape` and `num` as shown below. | |
| | num/shape | 16x32bx2/16x64b/32x32b | 16x128b | 16x256b | | |
| | --------- | ---------------------- | ------- | ------- | | |
| | x1 | 1 | 2 | 4 | | |
| | x2 | 2 | 4 | 8 | | |
| | x4 | 4 | 8 | 16 | | |
| | x8 | 8 | 16 | 32 | | |
| | x16 | 16 | 32 | 64 | | |
| | x32 | 32 | 64 | 128 | | |
| | x64 | 64 | 128 | NA | | |
| | x128 | 128 | NA | NA | | |
| The last argument `i1 %pack` is a compile-time constant which when set, | |
| indicates that the adjacent columns are packed into a single 32-bit element | |
| during the load | |
| `tcgen05.ld.red` contains `%redOp` flag to specify the load reduction operation | |
| and the f32 variant supports `%abs` and `%nan` bit flags for abs and nan | |
| respectively | |
| `%redOp` flag: | |
| | value | operation | | |
| | ----- | --------- | | |
| | 0 | min | | |
| | 1 | max | | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instructions-tcgen05-ld). | |
| #### '`llvm.nvvm.tcgen05.st.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.st.<shape>.<num>(ptr addrspace(6) %tmem_addr, <n x i32> %args, i1 %unpack) | |
| declare void @llvm.nvvm.tcgen05.st.16x32bx2.<num>(ptr addrspace(6) %tmem_addr, <n x i32> %args, i64 %offset, i1 %unpack) | |
| ``` | |
| ##### Overview: | |
| This group of intrinsics asynchronously store data from the source vector into | |
| the Tensor Memory at the location specified by the 32-bit address operand | |
| `tmem_addr` collectively across all threads of the warps. | |
| All the threads in the warp must specify the same value of `tmem_addr`, which | |
| must be the base address of the collective load operation. Otherwise, the | |
| behavior is undefined. | |
| The `shape` qualifier and the `num` qualifier together determines the total | |
| dimension of the data ('n') which is loaded from the Tensor Memory. The `shape` | |
| qualifier indicates the base dimension of data. The `num` qualifier indicates | |
| the repeat factor on the base dimension resulting in the total dimension of the | |
| data that is accessed. | |
| Allowed values for the 'num' are `x1, x2, x4, x8, x16, x32, x64, x128`. | |
| Allowed values for the 'shape' in the first intrinsic are | |
| `16x64b, 16x128b, 16x256b, 32x32b`. | |
| Allowed value for the 'shape' in the second intrinsic is `16x32bx2`. | |
| `args` argument is a vector consisting of one or more 32-bit registers derived | |
| from `shape` and `num` as listed in the table listed in the `tcgen05.ld` | |
| section. | |
| Each shape support an `unpack` mode to allow a 32-bit element in the register to | |
| be unpacked into two 16-bit elements and store them in adjacent columns. | |
| `unpack` mode can be enabled by setting the `%unpack` operand to 1 and can be | |
| disabled by setting it to 0. | |
| The last argument `i1 %unpack` is a compile-time constant which when set, | |
| indicates that a 32-bit element in the register to be unpacked into two 16-bit | |
| elements and store them in adjacent columns. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instructions-tcgen05-st). | |
| ### tcgen05.mma Intrinsics | |
| #### Overview | |
| `tcgen05.mma` operation of shape `M x N x K` perform matrix multiplication and | |
| accumulation of the form: `D = A * B + D` where: | |
| > - the `A` matrix has shape `M x K`, in either `Tensor Memory` or `Shared Memory` | |
| > - the `B` matrix has shape `K x N`, in `Shared Memory` of the current CTA and, optionally in peer CTA | |
| > - the `D` matrix is of the shape `M x N`, in `Tensor Memory` | |
| Optionally an input predicate can be used to disable the input (`%enable_inp_d`) | |
| from the accumulator matrix and the following operation can be performed as | |
| `D = A * B` | |
| The matrix multiplication and accumulation operations are categorized into | |
| various kinds based on input types and the throughput of the multiplication | |
| operation. The following table shows the different kinds of MMA operations | |
| that are supported: | |
| | .kind | Supported Input Types | | |
| | -------- | ---------------------------------------------------------------------- | | |
| | f16 | F16 and BF16 | | |
| | tf32 | TF32 | | |
| | f8f6f4 | All combinations of F8, F6, and F4 | | |
| | i8 | Signed and Unsigned 8-bit Integers | | |
| | ti16 | 16-bit integer types for non-block-scale operations | | |
| | mxf8f6f4 | MX-floating point formats | | |
| | mxf4 | MX-floating point formats (FP4) | | |
| | mxf4nvf4 | MXF4 + custom NVIDIA 4-bit floating point (with common scaling factor) | | |
| `tcgen05.mma.sp` supports sparse variant of `A` with shape `M x K` stored in | |
| packed form as `M X (K / 2)` in memory. The `%spmetadata` specifies the mapping | |
| of the `K / 2` non-zero elements to the `K` elements before performing the MMA | |
| operation. | |
| `tcgen05.mma.block_scale` perform matrix multiplication with block scaling | |
| `D = (A * scale_A) * (B * scale_B) + D` where scaling of input matrices from | |
| memory to form the matrix `A` and matrix `B` before performing the MMA | |
| operation. Scale factors for `A` and `B` matrices need to be duplicated to all | |
| 32 lane partitions of tensor memory. The shape of `%scale_a` and `%scale_b` | |
| matrices depend on the `.scale_vectorsize` described in | |
| [here](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-mma-scale-valid-comb) | |
| The sparsity metadata (`%spmetadata`) as well as the block-scale inputs for | |
| `A / B` matrices (`%scale_a` and `%scale_b`) reside in Tensor Memory. | |
| To facilitate opportunistic re-use of `A / B` matrix data across a sequence of | |
| MMA operations, the `A/B` matrices are loaded into collector buffers. The | |
| `tcgen05.mma`, `tcgen05.mma.disable_output_lane`, and | |
| `tcgen05.mma.block_scale` intrinsics use `%collector_usage_a_op_flag` and | |
| `%collector_usage_b_op_flag` to describe the usage of matrix `A` and matrix `B` | |
| collector buffers. The `tcgen05.mma.ws` intrinsics use | |
| `%collector_usage_b_buffer_flag` and `%collector_usage_b_op_flag` to describe | |
| the matrix `B` collector buffer. The flag value of the collector usage flag in | |
| the intrinsic specifies the nature of the re-use. | |
| There are three kinds of matrix descriptors used by the tcgen05 family of | |
| instructions: | |
| ```{list-table} | |
| :widths: 28 60 12 | |
| :header-rows: 1 | |
| * - Descriptor | |
| - Description | |
| - Size (bits) | |
| * - Shared Memory Descriptor | |
| - Describes properties of multiplicand matrix in shared memory, including | |
| its location within the CTA's shared memory. | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-shared-memory-descriptor) | |
| - 64 | |
| * - Instruction Descriptor | |
| - Describes shapes, types, and details of all matrices and the MMA | |
| operation. | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-zero-column-mask-descriptor) | |
| - 32 | |
| * - Zero-Column Mask Descriptor | |
| - Generates a mask specifying which columns of B matrix are zeroed in the | |
| MMA operation, regardless of values in shared memory. Total mask size = | |
| N bits | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instruction-descriptor) | |
| - 64 | |
| ``` | |
| `tcgen05.mma` can be used for general matrix multiplication or for convolution | |
| operations. In case of convolutions, the `activations` can be stored in either | |
| matrix `A` or matrix `B` while the `weights` will be stored in the other matrix | |
| `tcgen05.mma` has an optional collector qualifier to specify when an `A` or `B` | |
| matrix is new to the sequence and should be loaded, unchanged within the | |
| sequence and, should be reused, or the last use in the sequence and should be | |
| discarded. The collector qualifier is used to give the TensorCore permission to | |
| reuse a previously loaded `A` or `B` matrix; however reuse is opportunistic in | |
| that the TensorCore may reload a matrix even when it has permission to reuse | |
| that matrix. Thus, the source memory of an A or B matrix must not be modified | |
| while the MMA instruction using those matrices has not completed - regardless of | |
| collector qualifier permissions. Values other than `DISCARD` for the matrix `B` | |
| collector usage require support for the `.collector::b::...` PTX qualifier. | |
| The `cta_group::1` specifies that the operation is performed on the Tensor | |
| Memory of the executing thread’s CTA only. The `cta_group::2` specifies that the | |
| MMA operation is performed on the Tensor Memory of the executing thread’s CTA | |
| and its peer CTA. | |
| The vector operand `%disable_output_lane` specifies the lane(s) in the Tensor | |
| Memory that should be not be updated with the resultant matrix D. Elements of | |
| the vector operand disable-output-lane forms a mask where each bit corresponds | |
| to a lane of the Tensor Memory, with least significant bit of the first element | |
| of the vector (leftmost in syntax) corresponding to the lane 0 of the Tensor | |
| Memory. If a bit in the mask is 1, then the corresponding lane in the Tensor | |
| Memory for the resultant matrix D will not be updated | |
| #### Intrinsic Design: | |
| Given the broad feature set of `tcgen05.mma` instruction modeling these | |
| through intrinsics is highly complex, we've modeled the `tcgen05.mma` | |
| instructions using flag operands. We've added range checks to these flags to | |
| prevent invalid values. We also expanded some flags back into intrinsic | |
| modifiers to avoid supporting invalid combinations of features. These intrinsics | |
| capture ~2000+ individual PTX instruction combinations of this family. | |
| #### '`llvm.nvvm.tcgen05.mma.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.mma.shared(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; .sp variants | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; .scale_d variants | |
| declare void @llvm.nvvm.tcgen05.mma.shared.scale_d(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %scale_d_imm, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.scale_d<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %scale_d_imm, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; sp.scale_d variants | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.scale_d(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %scale_d_imm, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.scale_d<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %scale_d_imm, i32 %kind_flag, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ``` | |
| ##### Overview: | |
| `nvvm.tcgen05.mma` is an asynchronous intrinsic which initiates an `M x N x K` | |
| matrix multiply and accumulate operation, `D = A * B + D` where the `A` matrix | |
| is `M x K`, the `B` matrix is `K x N`, and the `D` matrix is `M x N`. The | |
| operation of the form `D = A*B` is issued when the input predicate argument | |
| `%enable_inp_d` is false. The optional immediate argument `%scale_d_imm` can be | |
| specified to scale the input matrix `D` as follows: | |
| `D = A * B + D * (2 ^ - %scale_d_imm)`. The valid range of values for argument | |
| `%scale_d_imm` is `[0, 15]`. The 32-bit register operand idesc is the | |
| instruction descriptor as described in | |
| [Instruction descriptor](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instruction-descriptor) | |
| `nvvm.tcgen05.mma` has single thread semantics, unlike the collective | |
| instructions `nvvm.mma.sync` or the PTX `wgmma.mma_async` instruction. So, a | |
| single thread issuing the `nvvm.tcgen05.mma` will result in the initiation of | |
| the whole matrix and accumulate operation | |
| When `.sp` is specifed, the dimension of A matrix is `M x (K/2)` and requires | |
| specifying an additional `%spmetadata` argument | |
| `.ashift` shifts the rows of the A matrix down by one row, except for the last | |
| row in the Tensor Memory. `.ashift` is only allowed with M = 128 or M = 256. | |
| The `%collector_usage_a_op_flag` and `%collector_usage_b_op_flag` flags specify | |
| the usage of collector buffers for matrix `A` and matrix `B`, respectively. It | |
| is illegal to specify either of `USE` or `FILL` for | |
| `%collector_usage_a_op_flag` along with `.ashift`. | |
| For more information, refer to the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-mma-instructions-mma) | |
| The following tables describe the possible values of the flag arguments | |
| `%kind_flag` flag: | |
| | `kind_flag` | value | | |
| | ----------- | ----- | | |
| | F16 | 0 | | |
| | TF32 | 1 | | |
| | F8F6F4 | 2 | | |
| | I8 | 3 | | |
| | TI16 | 4 | | |
| `%cta_group_flag` flag: | |
| | `cta_group_flag` | value | | |
| | ---------------- | ----- | | |
| | CG1 | 1 | | |
| | CG2 | 2 | | |
| `%collector_usage_a_op_flag` and `%collector_usage_b_op_flag` flags: | |
| | collector usage flag | value | | |
| | -------------------- | ----- | | |
| | DISCARD | 0 | | |
| | LASTUSE | 1 | | |
| | FILL | 2 | | |
| | USE | 3 | | |
| #### '`llvm.nvvm.tcgen05.mma.block_scale*`' | |
| ##### Syntax: | |
| ```llvm | |
| ; mxf8f6f4 | |
| declare void @llvm.nvvm.tcgen05.mma.shared.mxf8f6f4.block_scale(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.mxf8f6f4.block_scale(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.shared.mxf8f6f4.block_scale.block32(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.mxf8f6f4.block_scale.block32(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.mxf8f6f4.block_scale(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.mxf8f6f4.block_scale(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.mxf8f6f4.block_scale.block32(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.mxf8f6f4.block_scale.block32(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; mxf4 | |
| declare void @llvm.nvvm.tcgen05.mma.shared.mxf4.block_scale(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.mxf4.block_scale(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.shared.mxf4.block_scale.block32(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.mxf4.block_scale.block32(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.mxf4.block_scale(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.mxf4.block_scale(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.mxf4.block_scale.block32(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.mxf4.block_scale.block32(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; mxf4nvf4 | |
| declare void @llvm.nvvm.tcgen05.mma.shared.mxf4nvf4.block_scale.block16(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.mxf4nvf4.block_scale.block16(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.shared.mxf4nvf4.block_scale.block32(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.mxf4nvf4.block_scale.block32(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.mxf4nvf4.block_scale.block16(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.mxf4nvf4.block_scale.block16(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.mxf4nvf4.block_scale.block32(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.mxf4nvf4.block_scale.block32(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, ptr addrspace(6) %scale_a, ptr addrspace(6) %scale_b, i32 %cta_group_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ``` | |
| ##### Overview: | |
| `nvvm.tcgen05.mma.block_scale` is an asynchronous intrinsic which initiates | |
| an `M x N x K` matrix multiply and accumulate operation | |
| `D = (A * scale_a) * (B * scale_b) + D` where the `A` matrix is `M x K`, the | |
| `B` matrix is `K x N`, and the `D` matrix is `M x N`. The matrices `A` and `B` | |
| are scaled with `%scale_A` and `%scale_B` matrices respectively before | |
| performing the matrix multiply and accumulate operation. The operation of the | |
| form `D = A*B` is issued when the input predicate argument `%enable_inp_d` is | |
| false. The 32-bit register operand idesc is the instruction descriptor as | |
| described in [Instruction descriptor](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instruction-descriptor) | |
| `nvvm.tcgen05.mma.block_scale` has single thread semantics, unlike the | |
| collective instructions `nvvm.mma.sync` or the PTX `wgmma.mma_async` | |
| instruction. So, a single thread issuing the `nvvm.tcgen05.mma.block_scale` will | |
| result in the initiation of the whole matrix multiply and accumulate operation. | |
| When `.sp` is specified, the dimension of A matrix is `M x (K / 2)` and requires | |
| specifying an additional `%spmetadata` argument | |
| The `%collector_usage_a_op_flag` and `%collector_usage_b_op_flag` flags specify | |
| the usage of collector buffers for matrix `A` and matrix `B`, respectively. | |
| For more information, refer to the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-mma-instructions-mma) | |
| The following tables describe the possible values of the flag arguments | |
| `%cta_group_flag`: | |
| | `cta_group_flag` | value | | |
| | ---------------- | ----- | | |
| | CG1 | 1 | | |
| | CG2 | 2 | | |
| `%collector_usage_a_op_flag` and `%collector_usage_b_op_flag`: | |
| | collector usage flag | value | | |
| | -------------------- | ----- | | |
| | DISCARD | 0 | | |
| | LASTUSE | 1 | | |
| | FILL | 2 | | |
| | USE | 3 | | |
| #### '`llvm.nvvm.tcgen05.mma.disable_output_lane*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.tcgen05.mma.shared.disable_output_lane.cg1(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.shared.disable_output_lane.cg2(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.disable_output_lane.cg1<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.disable_output_lane.cg2<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; .sp variants | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.disable_output_lane.cg1(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.disable_output_lane.cg2(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.disable_output_lane.cg1<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.disable_output_lane.cg2<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; .scale_d variants | |
| declare void @llvm.nvvm.tcgen05.mma.shared.scale_d.disable_output_lane.cg1(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %scale_d_imm, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.shared.scale_d.disable_output_lane.cg2(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %scale_d_imm, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.scale_d.disable_output_lane.cg1<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %scale_d_imm, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.tensor.scale_d.disable_output_lane.cg2<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %scale_d_imm, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ; .sp.scale_d variants | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.scale_d.disable_output_lane.cg1(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %scale_d_imm, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.shared.scale_d.disable_output_lane.cg2(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %scale_d_imm, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.scale_d.disable_output_lane.cg1<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %scale_d_imm, <4 x i32> %disable_output_lane_v4, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.sp.tensor.scale_d.disable_output_lane.cg2<.ashift>(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %scale_d_imm, <8 x i32> %disable_output_lane_v8, i32 %kind_flag, i32 %collector_usage_a_op_flag, i32 %collector_usage_b_op_flag) | |
| ``` | |
| ##### Overview: | |
| `nvvm.tcgen05.mma.disable_output_lane` is an asynchronous intrinsic which | |
| initiates an `M x N x K` matrix multiply and accumulate operation | |
| `D = A * B + D` where the `A` matrix is `M x K`, the `B` matrix is `K x N`, and | |
| the `D` matrix is `M x N`. The operation of the form `D = A*B` is issued when | |
| the input predicate argument `%enable_inp_d` is false. The optional immediate | |
| argument `%scale_d_imm` can be specified to scale the input matrix `D` as | |
| follows: `D = A*B+D * (2 ^ - %scale_d_imm)`. The valid range of values for | |
| argument `%scale_d_imm` is `[0, 15]`. The 32-bit register operand idesc is the | |
| instruction descriptor as described in | |
| [Instruction descriptor](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instruction-descriptor) | |
| The vector operand `%disable_output_lane` specifies the lane(s) in the Tensor | |
| Memory that should be not be updated with the resultant matrix `D`. Elements of | |
| the vector operand `%disable_output_lane` forms a mask where each bit | |
| corresponds to a lane of the Tensor Memory, with least significant bit of the | |
| first element of the vector corresponding to the `lane 0` of the Tensor Memory. | |
| If a bit in the mask is 1, then the corresponding lane in the Tensor Memory for | |
| the resultant matrix `D` will not be updated | |
| `nvvm.tcgen05.mma.disable_output_lane` has single thread semantics, unlike the | |
| collective instructions `nvvm.mma.sync` or the PTX `wgmma.mma_async` | |
| instruction. So, a single thread issuing the | |
| `nvvm.tcgen05.mma.disable_output_lane` will result in the initiation of the | |
| whole matrix multiply and accumulate operation | |
| When `.sp` is specifed, the dimension of A matrix is `M x (K / 2)` and requires | |
| specifiying an additional `%spmetadata` argument. | |
| > `.ashift` shifts the rows of the A matrix down by one row, except for the last | |
| > row in the Tensor Memory. `.ashift` is only allowed with M = 128 or M = 256. | |
| The `%collector_usage_a_op_flag` and `%collector_usage_b_op_flag` flags specify | |
| the usage of collector buffers for matrix `A` and matrix `B`, respectively. It | |
| is illegal to specify either of `USE` or `FILL` for | |
| `%collector_usage_a_op_flag` along with `.ashift`. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-mma-instructions-mma) | |
| The following tables describes the possible values of the flag arguments | |
| `%kind_flag`: | |
| | `kind_flag` | value | | |
| | ----------- | ----- | | |
| | F16 | 0 | | |
| | TF32 | 1 | | |
| | F8F6F4 | 2 | | |
| | I8 | 3 | | |
| | TI16 | 4 | | |
| The `cg1` and `cg2` suffixes select CTA group 1 and CTA group 2, respectively. | |
| `%collector_usage_a_op_flag` and `%collector_usage_b_op_flag`: | |
| | collector usage flag | value | | |
| | -------------------- | ----- | | |
| | DISCARD | 0 | | |
| | LASTUSE | 1 | | |
| | FILL | 2 | | |
| | USE | 3 | | |
| #### '`llvm.nvvm.tcgen05.mma.ws*`' | |
| ##### Syntax: | |
| ```llvm | |
| // tcgen05.mma.ws | |
| declare void @llvm.nvvm.tcgen05.mma.ws.shared(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.tensor(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.shared.zero_col_mask(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %zero_col_mask, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.shared.zero_col_mask(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %zero_col_mask, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.tensor.zero_col_mask(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, i64 %zero_col_mask, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| ; .sp variants | |
| declare void @llvm.nvvm.tcgen05.mma.ws.sp.shared(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.sp.tensor(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.sp.shared.zero_col_mask(ptr addrspace(6) %d, i64 %adesc, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %zero_col_mask, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| declare void @llvm.nvvm.tcgen05.mma.ws.sp.tensor.zero_col_mask(ptr addrspace(6) %d, ptr addrspace(6) %atensor, i64 %bdesc, i32 %idesc, i1 %enable_inp_d, ptr addrspace(6) %spmetadata, i64 %zero_col_mask, i32 %kind_flag, i32 %collector_usage_b_buffer_flag, i32 %collector_usage_b_op_flag) | |
| ``` | |
| ##### Overview: | |
| `nvvm.tcgen05.mma.ws` is an asynchronous intrinsic which initiates an | |
| `M x N x K` weight stationary convolution matrix multiply and accumulate | |
| operation, `D = A * B + D` where the `A` matrix is `M x K`, the `B` matrix is | |
| `K x N`, and the `D` matrix is `M x N`. The operation of the form `D = A*B` is | |
| issued when the input predicate argument `%enable_inp_d` is false. The optional | |
| immediate argument `%scale_d_imm` can be specified to scale the input matrix `D` | |
| as follows: `D = A*B+D * (2 ^ - %scale_d_imm)`. The valid range of values for | |
| argument `%scale_d_imm` is `[0, 15]`. The 32-bit register operand idesc is the | |
| instruction descriptor as described in | |
| [Instruction descriptor](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-instruction-descriptor) | |
| `nvvm.tcgen05.mma` has single thread semantics, unlike the collective | |
| instructions `nvvm.mma.sync` or the PTX `wgmma.mma_async` instruction. So, a | |
| single thread issuing the `nvvm.tcgen05.mma` will result in the initiation of | |
| the whole matrix multiply and accumulate operation | |
| When `.sp` is specifed, the dimension of A matrix is `M x (K / 2)` and requires | |
| specifiying an additional `%spmetadata` argument | |
| The operand `%zero_col_mask` is a 64-bit register which specifies the | |
| [Zero-Column Mask Descriptor](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-zero-column-mask-descriptor). | |
| The zero-column mask descriptor is used to generate a mask that specifies which | |
| columns of `B` matrix will have zero value for the matrix multiply and | |
| accumulate operation regardless of the values present in the shared memory. | |
| The `%collector_usage_b_buffer_flag` and `%collector_usage_b_op_flag` together | |
| flag specifies the usage of collector buffer for Matrix `B`. | |
| For more information, refer to the | |
| [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#tcgen05-mma-instructions-mma-ws) | |
| The following tables describes the possible values of the flag arguments | |
| `%kind_flag`: | |
| | `kind_flag` | value | | |
| | ----------- | ----- | | |
| | F16 | 0 | | |
| | TF32 | 1 | | |
| | F8F6F4 | 2 | | |
| | I8 | 3 | | |
| | TI16 | 4 | | |
| `%collector_usage_b_buffer_flag`: | |
| | `collector_usage_b_buffer_flag` | value | | |
| | ------------------------------- | ----- | | |
| | B0 | 0 | | |
| | B1 | 1 | | |
| | B2 | 2 | | |
| | B3 | 3 | | |
| `%collector_usage_b_op_flag`: | |
| | `collector_usage_b_op_flag` | value | | |
| | --------------------------- | ----- | | |
| | DISCARD | 0 | | |
| | LASTUSE | 1 | | |
| | FILL | 2 | | |
| | USE | 3 | | |
| ### Store Intrinsics | |
| #### '`llvm.nvvm.st.bulk.*`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.st.bulk.p0.i32(ptr %dst, i32 %size, i64 immarg %initval) | |
| declare void @llvm.nvvm.st.bulk.p0.i64(ptr %dst, i64 %size, i64 immarg %initval) | |
| declare void @llvm.nvvm.st.bulk.p3.i32(ptr addrspace(3) %dst, i32 %size, i64 immarg %initval) | |
| declare void @llvm.nvvm.st.bulk.p3.i64(ptr addrspace(3) %dst, i64 %size, i64 immarg %initval) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.st.bulk.*`' intrinsics initialize a region of shared memory | |
| starting from the location specified by the destination address operand `%dst`. | |
| The integer operand `%size`, which may have type `i32` or `i64`, specifies the | |
| amount of memory to be initialized in terms of number of bytes and must be a | |
| multiple of 8. Otherwise, the behavior is undefined. | |
| The integer immediate operand `%initval` specifies the initialization value for | |
| the memory locations. The only numeric value allowed is 0. | |
| The `@llvm.nvvm.st.bulk.p3.*` and `@llvm.nvvm.st.bulk.p0.*` intrinsics are | |
| similar, but the latter uses generic addressing (see [Generic Addressing](https://docs.nvidia.com/cuda/parallel-thread-execution/#generic-addressing)). | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-st-bulk). | |
| #### '`llvm.nvvm.st.async`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.st.async.i32(ptr addrspace(7) %dest_addr, i32 %value, ptr addrspace(7) %mbarrier_addr) | |
| declare void @llvm.nvvm.st.async.i64(ptr addrspace(7) %dest_addr, i64 %value, ptr addrspace(7) %mbarrier_addr) | |
| declare void @llvm.nvvm.st.async.i128(ptr addrspace(7) %dest_addr, i128 %value, ptr addrspace(7) %mbarrier_addr) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.st.async`' intrinsic initiates a weak | |
| asynchronous store operation to shared memory that stores the value specified | |
| by the `%value` operand to the destination address specified by the | |
| `%dest_addr` operand. The `%value` operand must be an `i32`, `i64` or | |
| `i128`, lowering to the `.b32`, `.b64` and `.b128` variants | |
| of the `st.async` PTX instruction respectively. | |
| The store operation is treated as a weak memory operation. The effects of this | |
| operation become visible to other threads only when synchronization is | |
| established by other means. | |
| The operation is performed asynchronously and the completion is signalled using | |
| the mbarrier object specified by the `%mbarrier_addr` operand. Upon completion, | |
| a [complete-tx](https://docs.nvidia.com/cuda/parallel-thread-execution/#parallel-synchronization-and-communication-instructions-mbarrier-complete-tx-operation) operation is performed on the mbarrier object, with the | |
| `completeCount` argument equal to the amount of data stored in bytes. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-st-async). | |
| #### '`llvm.nvvm.st.async.{sys,gpu}`' | |
| ##### Syntax: | |
| ```llvm | |
| ; sys scope | |
| declare void @llvm.nvvm.st.async.sys.i8(ptr addrspace(1) %dest_addr, i8 %value, i1 immarg %is_multimem) | |
| declare void @llvm.nvvm.st.async.sys.i16(ptr addrspace(1) %dest_addr, i16 %value, i1 immarg %is_multimem) | |
| declare void @llvm.nvvm.st.async.sys.i32(ptr addrspace(1) %dest_addr, i32 %value, i1 immarg %is_multimem) | |
| declare void @llvm.nvvm.st.async.sys.i64(ptr addrspace(1) %dest_addr, i64 %value, i1 immarg %is_multimem) | |
| ; gpu scope | |
| declare void @llvm.nvvm.st.async.gpu.i8(ptr addrspace(1) %dest_addr, i8 %value, i1 immarg %is_multimem) | |
| declare void @llvm.nvvm.st.async.gpu.i16(ptr addrspace(1) %dest_addr, i16 %value, i1 immarg %is_multimem) | |
| declare void @llvm.nvvm.st.async.gpu.i32(ptr addrspace(1) %dest_addr, i32 %value, i1 immarg %is_multimem) | |
| declare void @llvm.nvvm.st.async.gpu.i64(ptr addrspace(1) %dest_addr, i64 %value, i1 immarg %is_multimem) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.st.async.sys`' and | |
| '`llvm.nvvm.st.async.gpu`' intrinsics initiate an | |
| asynchronous release store to global memory that stores the value specified by | |
| the `%value` operand to the destination address specified by the `%dest_addr` | |
| operand. | |
| The `%is_multimem` immediate argument selects the variant of the store. When it | |
| is `0`, a regular `st.async` is emitted. When it is `1`, a | |
| `multimem.st.async` is emitted and the `%dest_addr` operand must be a | |
| multimem address. | |
| The store carries `.release` semantics — prior stores from the current thread | |
| are made visible to other threads in the scope. | |
| The scope of this operation can be `sys` or `gpu`. | |
| These intrinsics lower to the `.b8`, `.b16`, `.b32`, and `.b64` variants of the | |
| `st.async` instruction. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-st-async). | |
| #### '`llvm.nvvm.st.async.mmio.sys`' | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.st.async.mmio.sys.i8(ptr addrspace(1) %dest_addr, i8 %value) | |
| declare void @llvm.nvvm.st.async.mmio.sys.i16(ptr addrspace(1) %dest_addr, i16 %value) | |
| declare void @llvm.nvvm.st.async.mmio.sys.i32(ptr addrspace(1) %dest_addr, i32 %value) | |
| declare void @llvm.nvvm.st.async.mmio.sys.i64(ptr addrspace(1) %dest_addr, i64 %value) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.st.async.mmio.sys`' intrinsic performs an [MMIO](https://docs.nvidia.com/cuda/parallel-thread-execution/#mmio-operation) | |
| store to global memory with `.release` semantics at the `sys` scope. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/#data-movement-and-conversion-instructions-st-async). | |
| ### clusterlaunchcontrol Intrinsics | |
| #### '`llvm.nvvm.clusterlaunchcontrol.try_cancel*`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.clusterlaunchcontrol.try_cancel.async.shared(ptr addrspace(3) %addr, ptr addrspace(3) %mbar) | |
| declare void @llvm.nvvm.clusterlaunchcontrol.try_cancel.async.multicast.shared(ptr addrspace(3) %addr, ptr addrspace(3) %mbar) | |
| ``` | |
| ##### Overview: | |
| The `clusterlaunchcontrol.try_cancel` intrinsics requests atomically cancelling | |
| the launch of a cluster that has not started running yet. It asynchronously | |
| non-atomically writes a 16-byte opaque response to shared memory, pointed to by | |
| 16-byte-aligned `addr` indicating whether the operation succeeded or failed. | |
| `addr` and 8-byte-aligned `mbar` must refer to `shared::cta` otherwise the | |
| behavior is undefined. The completion of the asynchronous operation is tracked | |
| using the mbarrier completion mechanism at `.cluster` scope referenced by the | |
| shared memory pointer, `mbar`. On success, the opaque response contains the | |
| CTA id of the first CTA of the canceled cluster; no other successful response | |
| from other `clusterlaunchcontrol.try_cancel` operations from the same grid | |
| will contain that id. | |
| The `multicast` variant specifies that the response is asynchronously | |
| non-atomically written to the corresponding shared memory location of each CTA | |
| in the requesting cluster. The completion of the write of each local response is | |
| tracked by independent mbarriers at the corresponding shared memory location of | |
| each CTA in theccluster. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/?a#parallel-synchronization-and-communication-instructions-clusterlaunchcontrol-try-cancel). | |
| #### '`llvm.nvvm.clusterlaunchcontrol.query_cancel.is_canceled`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare i1 @llvm.nvvm.clusterlaunchcontrol.query_cancel.is_canceled(i128 %try_cancel_response) | |
| ``` | |
| ##### Overview: | |
| The `llvm.nvvm.clusterlaunchcontrol.query_cancel.is_canceled` intrinsic | |
| decodes the opaque response written by the | |
| `llvm.nvvm.clusterlaunchcontrol.try_cancel` operation. | |
| The intrinsic returns `0` (false) if the request failed. If the request | |
| succeeded, it returns `1` (true). A true result indicates that: | |
| - the thread block cluster whose first CTA id matches that of the response | |
| handle will not run, and | |
| - no other successful response of another `try_cancel` request in the grid | |
| will contain the first CTA id of that cluster | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/?a#parallel-synchronization-and-communication-instructions-clusterlaunchcontrol-query-cancel). | |
| #### '`llvm.nvvm.clusterlaunchcontrol.query_cancel.get_first_ctaid.*`' Intrinsics | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.clusterlaunchcontrol.query_cancel.get_first_ctaid.x(i128 %try_cancel_response) | |
| declare i32 @llvm.nvvm.clusterlaunchcontrol.query_cancel.get_first_ctaid.y(i128 %try_cancel_response) | |
| declare i32 @llvm.nvvm.clusterlaunchcontrol.query_cancel.get_first_ctaid.z(i128 %try_cancel_response) | |
| ``` | |
| ##### Overview: | |
| The `clusterlaunchcontrol.query_cancel.get_first_ctaid.*` intrinsic can be | |
| used to decode the successful opaque response written by the | |
| `llvm.nvvm.clusterlaunchcontrol.try_cancel` operation. | |
| If the request succeeded: | |
| - `llvm.nvvm.clusterlaunchcontrol.query_cancel.get_first_ctaid.{x,y,z}` | |
| returns the coordinate of the first CTA in the canceled cluster, either x, y, | |
| or z. | |
| If the request failed, the behavior of these intrinsics is undefined. | |
| For more information, refer [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/?a#parallel-synchronization-and-communication-instructions-clusterlaunchcontrol-query-cancel). | |
| ### Perf Monitor Event Intrinsics | |
| #### '`llvm.nvvm.pm.event.mask`' Intrinsic | |
| ##### Syntax: | |
| ```llvm | |
| declare void @llvm.nvvm.pm.event.mask(i16 immarg %mask_val) | |
| ``` | |
| ##### Overview: | |
| The '`llvm.nvvm.pm.event.mask`' intrinsic triggers one or more performance | |
| monitor events. Each bit in the 16-bit immediate operand `%mask_val` controls | |
| an event. | |
| For more information on the pmevent instructions, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#miscellaneous-instructions-pmevent). | |
| ### Warp-level Matrix Transpose Intrinsics | |
| #### '`llvm.nvvm.movmatrix.sync.aligned.m8n8.trans.b16`' | |
| ##### Syntax: | |
| ```llvm | |
| declare i32 @llvm.nvvm.movmatrix.sync.aligned.m8n8.trans.b16(i32 %src) | |
| ``` | |
| ##### Overview: | |
| The '`@llvm.nvvm.movmatrix.sync.aligned.m8n8.trans.b16`' intrinsic | |
| transposes an 8x8 matrix of 16-bit elements distributed across all 32 | |
| threads of a warp. Each thread provides a 32-bit register containing two | |
| packed `.b16` elements, and receives back two packed `.b16` elements | |
| from the transposed matrix in the same format. | |
| The mandatory `.sync` qualifier indicates that `movmatrix` causes the | |
| executing thread to wait until all threads in the warp execute the same | |
| `movmatrix` intrinsic before resuming execution. | |
| The mandatory `.aligned` qualifier indicates that all threads in the warp | |
| must execute the same `movmatrix` intrinsic. In conditionally executed | |
| code, a `movmatrix` intrinsic should only be used if it is known that | |
| all threads in the warp evaluate the condition identically, otherwise the | |
| behavior is undefined. | |
| For more information, refer to the [PTX ISA](https://docs.nvidia.com/cuda/parallel-thread-execution/index.html#warp-level-matrix-instructions-movmatrix). | |
| ### Other Intrinsics | |
| For the full set of NVPTX intrinsics, please see the | |
| `include/llvm/IR/IntrinsicsNVVM.td` file in the LLVM source tree. | |
| (libdevice)= | |
| ## Linking with Libdevice | |
| The CUDA Toolkit comes with an LLVM bitcode library called `libdevice` that | |
| implements many common mathematical functions. This library can be used as a | |
| high-performance math library for any compilers using the LLVM NVPTX target. | |
| The library can be found under `nvvm/libdevice/` in the CUDA Toolkit and | |
| there is a separate version for each compute architecture. | |
| For a list of all math functions implemented in libdevice, see | |
| [libdevice Users Guide](http://docs.nvidia.com/cuda/libdevice-users-guide/index.html). | |
| To accommodate various math-related compiler flags that can affect code | |
| generation of libdevice code, the library code depends on a special LLVM IR | |
| pass (`NVVMReflect`) to handle conditional compilation within LLVM IR. This | |
| pass looks for calls to the `@__nvvm_reflect` function and replaces them | |
| with constants based on the defined reflection parameters. Such conditional | |
| code often follows a pattern: | |
| ```c++ | |
| float my_function(float a) { | |
| if (__nvvm_reflect("FASTMATH")) | |
| return my_function_fast(a); | |
| else | |
| return my_function_precise(a); | |
| } | |
| ``` | |
| The default value for all unspecified reflection parameters is zero. | |
| The `NVVMReflect` pass should be executed early in the optimization | |
| pipeline, immediately after the link stage. The `internalize` pass is also | |
| recommended to remove unused math functions from the resulting PTX. For an | |
| input IR module `module.bc`, the following compilation flow is recommended: | |
| The `NVVMReflect` pass will attempt to remove dead code even without | |
| optimizations. This allows potentially incompatible instructions to be avoided | |
| at all optimizations levels by using the `__CUDA_ARCH` argument. | |
| 1. Save list of external functions in `module.bc` | |
| 2. Link `module.bc` with `libdevice.compute_XX.YY.bc` | |
| 3. Internalize all functions not in list from (1) | |
| 4. Eliminate all unused internal functions | |
| 5. Run `NVVMReflect` pass | |
| 6. Run standard optimization pipeline | |
| :::{note} | |
| `linkonce` and `linkonce_odr` linkage types are not suitable for the | |
| libdevice functions. It is possible to link two IR modules that have been | |
| linked against libdevice using different reflection variables. | |
| ::: | |
| Since the `NVVMReflect` pass replaces conditionals with constants, it will | |
| often leave behind dead code of the form: | |
| ```llvm | |
| entry: | |
| .. | |
| br i1 true, label %foo, label %bar | |
| foo: | |
| .. | |
| bar: | |
| ; Dead code | |
| .. | |
| ``` | |
| Therefore, it is recommended that `NVVMReflect` is executed early in the | |
| optimization pipeline before dead-code elimination. | |
| The NVPTX TargetMachine knows how to schedule `NVVMReflect` at the beginning | |
| of your pass manager; just use the following code when setting up your pass | |
| manager and the PassBuilder will use `registerPassBuilderCallbacks` to let | |
| NVPTXTargetMachine::registerPassBuilderCallbacks add the pass to the | |
| pass manager: | |
| ```c++ | |
| std::unique_ptr<TargetMachine> TM = ...; | |
| PassBuilder PB(TM); | |
| ModulePassManager MPM; | |
| PB.parsePassPipeline(MPM, ...); | |
| ``` | |
| ### Reflection Parameters | |
| The libdevice library currently uses the following reflection parameters to | |
| control code generation: | |
| | Flag | Description | | |
| | ------------------ | ------------------------------------------------------ | | |
| | `__CUDA_FTZ=[0,1]` | Use optimized code paths that flush subnormals to zero | | |
| The value of this flag is determined by the "nvvm-reflect-ftz" module flag. | |
| The following sets the ftz flag to 1. | |
| ```llvm | |
| !llvm.module.flags = !{!0} | |
| !0 = !{i32 4, !"nvvm-reflect-ftz", i32 1} | |
| ``` | |
| (`i32 4` indicates that the value set here overrides the value in another | |
| module we link with. See the [LangRef](LangRef.md#module-flags-metadata) | |
| for details.) | |
| ## Executing PTX | |
| The most common way to execute PTX assembly on a GPU device is to use the CUDA | |
| Driver API. This API is a low-level interface to the GPU driver and allows for | |
| JIT compilation of PTX code to native GPU machine code. | |
| Initializing the Driver API: | |
| ```c++ | |
| CUdevice device; | |
| CUcontext context; | |
| // Initialize the driver API | |
| cuInit(0); | |
| // Get a handle to the first compute device | |
| cuDeviceGet(&device, 0); | |
| // Create a compute device context | |
| cuCtxCreate(&context, 0, device); | |
| ``` | |
| JIT compiling a PTX string to a device binary: | |
| ```c++ | |
| CUmodule module; | |
| CUfunction function; | |
| // JIT compile a null-terminated PTX string | |
| cuModuleLoadData(&module, (void*)PTXString); | |
| // Get a handle to the "myfunction" kernel function | |
| cuModuleGetFunction(&function, module, "myfunction"); | |
| ``` | |
| For full examples of executing PTX assembly, please see the [CUDA Samples](https://developer.nvidia.com/cuda-downloads) distribution. | |
| ## Common Issues | |
| ### ptxas complains of undefined function: \_\_nvvm_reflect | |
| When linking with libdevice, the `NVVMReflect` pass must be used. See | |
| {ref}`libdevice` for more information. | |
| ## Tutorial: A Simple Compute Kernel | |
| To start, let us take a look at a simple compute kernel written directly in | |
| LLVM IR. The kernel implements vector addition, where each thread computes one | |
| element of the output vector C from the input vectors A and B. To make this | |
| easier, we also assume that only a single CTA (thread block) will be launched, | |
| and that it will be one dimensional. | |
| ### The Kernel | |
| ```llvm | |
| target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64" | |
| target triple = "nvptx64-nvidia-cuda" | |
| ; Intrinsic to read X component of thread ID | |
| declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() readnone nounwind | |
| define ptx_kernel void @kernel(ptr addrspace(1) %A, | |
| ptr addrspace(1) %B, | |
| ptr addrspace(1) %C) { | |
| entry: | |
| ; What is my ID? | |
| %id = tail call i32 @llvm.nvvm.read.ptx.sreg.tid.x() readnone nounwind | |
| ; Compute pointers into A, B, and C | |
| %ptrA = getelementptr float, ptr addrspace(1) %A, i32 %id | |
| %ptrB = getelementptr float, ptr addrspace(1) %B, i32 %id | |
| %ptrC = getelementptr float, ptr addrspace(1) %C, i32 %id | |
| ; Read A, B | |
| %valA = load float, ptr addrspace(1) %ptrA, align 4 | |
| %valB = load float, ptr addrspace(1) %ptrB, align 4 | |
| ; Compute C = A + B | |
| %valC = fadd float %valA, %valB | |
| ; Store back to C | |
| store float %valC, ptr addrspace(1) %ptrC, align 4 | |
| ret void | |
| } | |
| ``` | |
| We can use the LLVM `llc` tool to directly run the NVPTX code generator: | |
| ```text | |
| # llc -mcpu=sm_20 kernel.ll -o kernel.ptx | |
| ``` | |
| :::{note} | |
| If you want to generate 32-bit code, change `p:64:64:64` to `p:32:32:32` | |
| in the module data layout string and use `nvptx-nvidia-cuda` as the | |
| target triple. | |
| ::: | |
| The output we get from `llc` (as of LLVM 3.4): | |
| ```text | |
| // | |
| // Generated by LLVM NVPTX Back-End | |
| // | |
| .version 3.1 | |
| .target sm_20 | |
| .address_size 64 | |
| // .globl kernel | |
| // @kernel | |
| .visible .entry kernel( | |
| .param .u64 kernel_param_0, | |
| .param .u64 kernel_param_1, | |
| .param .u64 kernel_param_2 | |
| ) | |
| { | |
| .reg .f32 %f<4>; | |
| .reg .s32 %r<2>; | |
| .reg .s64 %rl<8>; | |
| // %bb.0: // %entry | |
| ld.param.u64 %rl1, [kernel_param_0]; | |
| mov.u32 %r1, %tid.x; | |
| mul.wide.s32 %rl2, %r1, 4; | |
| add.s64 %rl3, %rl1, %rl2; | |
| ld.param.u64 %rl4, [kernel_param_1]; | |
| add.s64 %rl5, %rl4, %rl2; | |
| ld.param.u64 %rl6, [kernel_param_2]; | |
| add.s64 %rl7, %rl6, %rl2; | |
| ld.global.f32 %f1, [%rl3]; | |
| ld.global.f32 %f2, [%rl5]; | |
| add.f32 %f3, %f1, %f2; | |
| st.global.f32 [%rl7], %f3; | |
| ret; | |
| } | |
| ``` | |
| ### Dissecting the Kernel | |
| Now let us dissect the LLVM IR that makes up this kernel. | |
| #### Data Layout | |
| The data layout string determines the size in bits of common data types, their | |
| ABI alignment, and their storage size. For NVPTX, you should use one of the | |
| following: | |
| 32-bit PTX: | |
| ```llvm | |
| target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64" | |
| ``` | |
| 64-bit PTX: | |
| ```llvm | |
| target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64" | |
| ``` | |
| #### Target Intrinsics | |
| In this example, we use the `@llvm.nvvm.read.ptx.sreg.tid.x` intrinsic to | |
| read the X component of the current thread's ID, which corresponds to a read | |
| of register `%tid.x` in PTX. The NVPTX back-end supports a large set of | |
| intrinsics. A short list is shown below; please see | |
| `include/llvm/IR/IntrinsicsNVVM.td` for the full list. | |
| | Intrinsic | CUDA Equivalent | | |
| | --------------------------------------------- | ----------------- | | |
| | `i32 @llvm.nvvm.read.ptx.sreg.tid.{x,y,z}` | threadIdx.{x,y,z} | | |
| | `i32 @llvm.nvvm.read.ptx.sreg.ctaid.{x,y,z}` | blockIdx.{x,y,z} | | |
| | `i32 @llvm.nvvm.read.ptx.sreg.ntid.{x,y,z}` | blockDim.{x,y,z} | | |
| | `i32 @llvm.nvvm.read.ptx.sreg.nctaid.{x,y,z}` | gridDim.{x,y,z} | | |
| | `void @llvm.nvvm.barrier0()` | \_\_syncthreads() | | |
| #### Address Spaces | |
| You may have noticed that all of the pointer types in the LLVM IR example had | |
| an explicit address space specifier. What is address space 1? NVIDIA GPU | |
| devices (generally) have four types of memory: | |
| - Global: Large, off-chip memory | |
| - Shared: Small, on-chip memory shared among all threads in a CTA | |
| - Local: Per-thread, private memory | |
| - Constant: Read-only memory shared across all threads | |
| These different types of memory are represented in LLVM IR as address spaces. | |
| There is also a fifth address space used by the NVPTX code generator that | |
| corresponds to the "generic" address space. This address space can represent | |
| addresses in any other address space (with a few exceptions). This allows | |
| users to write IR functions that can load/store memory using the same | |
| instructions. Intrinsics are provided to convert pointers between the generic | |
| and non-generic address spaces. | |
| See {ref}`address-spaces` and {ref}`nvptx-intrinsics` for more information. | |
| ### Running the Kernel | |
| Generating PTX from LLVM IR is all well and good, but how do we execute it on | |
| a real GPU device? The CUDA Driver API provides a convenient mechanism for | |
| loading and JIT compiling PTX to a native GPU device, and launching a kernel. | |
| The API is similar to OpenCL. A simple example showing how to load and | |
| execute our vector addition code is shown below. Note that for brevity this | |
| code does not perform much error checking! | |
| :::{note} | |
| You can also use the `ptxas` tool provided by the CUDA Toolkit to offline | |
| compile PTX to machine code (SASS) for a specific GPU architecture. Such | |
| binaries can be loaded by the CUDA Driver API in the same way as PTX. This | |
| can be useful for reducing startup time by precompiling the PTX kernels. | |
| ::: | |
| ```c++ | |
| #include <iostream> | |
| #include <fstream> | |
| #include <cassert> | |
| #include "cuda.h" | |
| void checkCudaErrors(CUresult err) { | |
| assert(err == CUDA_SUCCESS); | |
| } | |
| /// main - Program entry point | |
| int main(int argc, char **argv) { | |
| CUdevice device; | |
| CUmodule cudaModule; | |
| CUcontext context; | |
| CUfunction function; | |
| CUlinkState linker; | |
| int devCount; | |
| // CUDA initialization | |
| checkCudaErrors(cuInit(0)); | |
| checkCudaErrors(cuDeviceGetCount(&devCount)); | |
| checkCudaErrors(cuDeviceGet(&device, 0)); | |
| char name[128]; | |
| checkCudaErrors(cuDeviceGetName(name, 128, device)); | |
| std::cout << "Using CUDA Device [0]: " << name << "\n"; | |
| int devMajor, devMinor; | |
| checkCudaErrors(cuDeviceComputeCapability(&devMajor, &devMinor, device)); | |
| std::cout << "Device Compute Capability: " | |
| << devMajor << "." << devMinor << "\n"; | |
| if (devMajor < 2) { | |
| std::cerr << "ERROR: Device 0 is not SM 2.0 or greater\n"; | |
| return 1; | |
| } | |
| std::ifstream t("kernel.ptx"); | |
| if (!t.is_open()) { | |
| std::cerr << "kernel.ptx not found\n"; | |
| return 1; | |
| } | |
| std::string str((std::istreambuf_iterator<char>(t)), | |
| std::istreambuf_iterator<char>()); | |
| // Create driver context | |
| checkCudaErrors(cuCtxCreate(&context, 0, device)); | |
| // Create module for object | |
| checkCudaErrors(cuModuleLoadDataEx(&cudaModule, str.c_str(), 0, 0, 0)); | |
| // Get kernel function | |
| checkCudaErrors(cuModuleGetFunction(&function, cudaModule, "kernel")); | |
| // Device data | |
| CUdeviceptr devBufferA; | |
| CUdeviceptr devBufferB; | |
| CUdeviceptr devBufferC; | |
| checkCudaErrors(cuMemAlloc(&devBufferA, sizeof(float)*16)); | |
| checkCudaErrors(cuMemAlloc(&devBufferB, sizeof(float)*16)); | |
| checkCudaErrors(cuMemAlloc(&devBufferC, sizeof(float)*16)); | |
| float* hostA = new float[16]; | |
| float* hostB = new float[16]; | |
| float* hostC = new float[16]; | |
| // Populate input | |
| for (unsigned i = 0; i != 16; ++i) { | |
| hostA[i] = (float)i; | |
| hostB[i] = (float)(2*i); | |
| hostC[i] = 0.0f; | |
| } | |
| checkCudaErrors(cuMemcpyHtoD(devBufferA, &hostA[0], sizeof(float)*16)); | |
| checkCudaErrors(cuMemcpyHtoD(devBufferB, &hostB[0], sizeof(float)*16)); | |
| unsigned blockSizeX = 16; | |
| unsigned blockSizeY = 1; | |
| unsigned blockSizeZ = 1; | |
| unsigned gridSizeX = 1; | |
| unsigned gridSizeY = 1; | |
| unsigned gridSizeZ = 1; | |
| // Kernel parameters | |
| void *KernelParams[] = { &devBufferA, &devBufferB, &devBufferC }; | |
| std::cout << "Launching kernel\n"; | |
| // Kernel launch | |
| checkCudaErrors(cuLaunchKernel(function, gridSizeX, gridSizeY, gridSizeZ, | |
| blockSizeX, blockSizeY, blockSizeZ, | |
| 0, NULL, KernelParams, NULL)); | |
| // Retrieve device data | |
| checkCudaErrors(cuMemcpyDtoH(&hostC[0], devBufferC, sizeof(float)*16)); | |
| std::cout << "Results:\n"; | |
| for (unsigned i = 0; i != 16; ++i) { | |
| std::cout << hostA[i] << " + " << hostB[i] << " = " << hostC[i] << "\n"; | |
| } | |
| // Clean up after ourselves | |
| delete [] hostA; | |
| delete [] hostB; | |
| delete [] hostC; | |
| // Clean-up | |
| checkCudaErrors(cuMemFree(devBufferA)); | |
| checkCudaErrors(cuMemFree(devBufferB)); | |
| checkCudaErrors(cuMemFree(devBufferC)); | |
| checkCudaErrors(cuModuleUnload(cudaModule)); | |
| checkCudaErrors(cuCtxDestroy(context)); | |
| return 0; | |
| } | |
| ``` | |
| You will need to link with the CUDA driver and specify the path to cuda.h. | |
| ```text | |
| # clang++ sample.cpp -o sample -O2 -g -I/usr/local/cuda-5.5/include -lcuda | |
| ``` | |
| We don't need to specify a path to `libcuda.so` since this is installed in a | |
| system location by the driver, not the CUDA toolkit. | |
| If everything goes as planned, you should see the following output when | |
| running the compiled program: | |
| ```text | |
| Using CUDA Device [0]: GeForce GTX 680 | |
| Device Compute Capability: 3.0 | |
| Launching kernel | |
| Results: | |
| 0 + 0 = 0 | |
| 1 + 2 = 3 | |
| 2 + 4 = 6 | |
| 3 + 6 = 9 | |
| 4 + 8 = 12 | |
| 5 + 10 = 15 | |
| 6 + 12 = 18 | |
| 7 + 14 = 21 | |
| 8 + 16 = 24 | |
| 9 + 18 = 27 | |
| 10 + 20 = 30 | |
| 11 + 22 = 33 | |
| 12 + 24 = 36 | |
| 13 + 26 = 39 | |
| 14 + 28 = 42 | |
| 15 + 30 = 45 | |
| ``` | |
| :::{note} | |
| You will likely see a different device identifier based on your hardware | |
| ::: | |
| ## Tutorial: Linking with Libdevice | |
| In this tutorial, we show a simple example of linking LLVM IR with the | |
| libdevice library. We will use the same kernel as the previous tutorial, | |
| except that we will compute `C = pow(A, B)` instead of `C = A + B`. | |
| Libdevice provides an `__nv_powf` function that we will use. | |
| ```llvm | |
| target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64" | |
| target triple = "nvptx64-nvidia-cuda" | |
| ; Intrinsic to read X component of thread ID | |
| declare i32 @llvm.nvvm.read.ptx.sreg.tid.x() readnone nounwind | |
| ; libdevice function | |
| declare float @__nv_powf(float, float) | |
| define ptx_kernel void @kernel(ptr addrspace(1) %A, | |
| ptr addrspace(1) %B, | |
| ptr addrspace(1) %C) { | |
| entry: | |
| ; What is my ID? | |
| %id = tail call i32 @llvm.nvvm.read.ptx.sreg.tid.x() readnone nounwind | |
| ; Compute pointers into A, B, and C | |
| %ptrA = getelementptr float, ptr addrspace(1) %A, i32 %id | |
| %ptrB = getelementptr float, ptr addrspace(1) %B, i32 %id | |
| %ptrC = getelementptr float, ptr addrspace(1) %C, i32 %id | |
| ; Read A, B | |
| %valA = load float, ptr addrspace(1) %ptrA, align 4 | |
| %valB = load float, ptr addrspace(1) %ptrB, align 4 | |
| ; Compute C = pow(A, B) | |
| %valC = call float @__nv_powf(float %valA, float %valB) | |
| ; Store back to C | |
| store float %valC, ptr addrspace(1) %ptrC, align 4 | |
| ret void | |
| } | |
| ``` | |
| To compile this kernel, we perform the following steps: | |
| 1. Link with libdevice | |
| 2. Internalize all but the public kernel function | |
| 3. Run `NVVMReflect` and set `__CUDA_FTZ` to 0 | |
| 4. Optimize the linked module | |
| 5. Codegen the module | |
| These steps can be performed by the LLVM `llvm-link`, `opt`, and `llc` | |
| tools. In a complete compiler, these steps can also be performed entirely | |
| programmatically by setting up an appropriate pass configuration (see | |
| {ref}`libdevice`). | |
| ```text | |
| # llvm-link t2.bc libdevice.compute_20.10.bc -o t2.linked.bc | |
| # opt -internalize -internalize-public-api-list=kernel -nvvm-reflect-list=__CUDA_FTZ=0 -nvvm-reflect -O3 t2.linked.bc -o t2.opt.bc | |
| # llc -mcpu=sm_20 t2.opt.bc -o t2.ptx | |
| ``` | |
| :::{note} | |
| The `-nvvm-reflect-list=_CUDA_FTZ=0` is not strictly required, as any | |
| undefined variables will default to zero. It is shown here for evaluation | |
| purposes. | |
| ::: | |
| This gives us the following PTX (excerpt): | |
| ```text | |
| // | |
| // Generated by LLVM NVPTX Back-End | |
| // | |
| .version 3.1 | |
| .target sm_20 | |
| .address_size 64 | |
| // .globl kernel | |
| // @kernel | |
| .visible .entry kernel( | |
| .param .u64 kernel_param_0, | |
| .param .u64 kernel_param_1, | |
| .param .u64 kernel_param_2 | |
| ) | |
| { | |
| .reg .pred %p<30>; | |
| .reg .f32 %f<111>; | |
| .reg .s32 %r<21>; | |
| .reg .s64 %rl<8>; | |
| // %bb.0: // %entry | |
| ld.param.u64 %rl2, [kernel_param_0]; | |
| mov.u32 %r3, %tid.x; | |
| ld.param.u64 %rl3, [kernel_param_1]; | |
| mul.wide.s32 %rl4, %r3, 4; | |
| add.s64 %rl5, %rl2, %rl4; | |
| ld.param.u64 %rl6, [kernel_param_2]; | |
| add.s64 %rl7, %rl3, %rl4; | |
| add.s64 %rl1, %rl6, %rl4; | |
| ld.global.f32 %f1, [%rl5]; | |
| ld.global.f32 %f2, [%rl7]; | |
| setp.eq.f32 %p1, %f1, 0f3F800000; | |
| setp.eq.f32 %p2, %f2, 0f00000000; | |
| or.pred %p3, %p1, %p2; | |
| @%p3 bra BB0_1; | |
| bra.uni BB0_2; | |
| BB0_1: | |
| mov.f32 %f110, 0f3F800000; | |
| st.global.f32 [%rl1], %f110; | |
| ret; | |
| BB0_2: // %__nv_isnanf.exit.i | |
| abs.f32 %f4, %f1; | |
| setp.gtu.f32 %p4, %f4, 0f7F800000; | |
| @%p4 bra BB0_4; | |
| // %bb.3: // %__nv_isnanf.exit5.i | |
| abs.f32 %f5, %f2; | |
| setp.le.f32 %p5, %f5, 0f7F800000; | |
| @%p5 bra BB0_5; | |
| BB0_4: // %.critedge1.i | |
| add.f32 %f110, %f1, %f2; | |
| st.global.f32 [%rl1], %f110; | |
| ret; | |
| BB0_5: // %__nv_isinff.exit.i | |
| ... | |
| BB0_26: // %__nv_truncf.exit.i.i.i.i.i | |
| mul.f32 %f90, %f107, 0f3FB8AA3B; | |
| cvt.rzi.f32.f32 %f91, %f90; | |
| mov.f32 %f92, 0fBF317200; | |
| fma.rn.f32 %f93, %f91, %f92, %f107; | |
| mov.f32 %f94, 0fB5BFBE8E; | |
| fma.rn.f32 %f95, %f91, %f94, %f93; | |
| mul.f32 %f89, %f95, 0f3FB8AA3B; | |
| // inline asm | |
| ex2.approx.ftz.f32 %f88,%f89; | |
| // inline asm | |
| add.f32 %f96, %f91, 0f00000000; | |
| ex2.approx.f32 %f97, %f96; | |
| mul.f32 %f98, %f88, %f97; | |
| setp.lt.f32 %p15, %f107, 0fC2D20000; | |
| selp.f32 %f99, 0f00000000, %f98, %p15; | |
| setp.gt.f32 %p16, %f107, 0f42D20000; | |
| selp.f32 %f110, 0f7F800000, %f99, %p16; | |
| setp.eq.f32 %p17, %f110, 0f7F800000; | |
| @%p17 bra BB0_28; | |
| // %bb.27: | |
| fma.rn.f32 %f110, %f110, %f108, %f110; | |
| BB0_28: // %__internal_accurate_powf.exit.i | |
| setp.lt.f32 %p18, %f1, 0f00000000; | |
| setp.eq.f32 %p19, %f3, 0f3F800000; | |
| and.pred %p20, %p18, %p19; | |
| @!%p20 bra BB0_30; | |
| bra.uni BB0_29; | |
| BB0_29: | |
| mov.b32 %r9, %f110; | |
| xor.b32 %r10, %r9, -2147483648; | |
| mov.b32 %f110, %r10; | |
| BB0_30: // %__nv_powf.exit | |
| st.global.f32 [%rl1], %f110; | |
| ret; | |
| } | |
| ``` |