Skip to content

Commit e57575b

Browse files
[lang] Add load/store matrix intrinsics
Signed-off-by: Asher Mancinelli <amancinelli@nvidia.com>
1 parent 3002c7f commit e57575b

8 files changed

Lines changed: 452 additions & 14 deletions

File tree

experimental/cuda-lang/docs/source/operations.rst

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,25 @@ Warp shuffle
171171
shfl_xor_sync
172172

173173

174+
Warp Matrix Load and Store
175+
--------------------------
176+
.. autosummary::
177+
:toctree: generated
178+
:nosignatures:
179+
180+
load_matrix
181+
store_matrix
182+
183+
.. autosummary::
184+
:toctree: generated
185+
:nosignatures:
186+
:template: autosummary/class_no_init.rst
187+
188+
MatrixLoadShape
189+
MatrixStoreShape
190+
MatrixLoadSourceFormat
191+
192+
174193
.. _operations-tensor-map:
175194

176195
TensorMap

experimental/cuda-lang/src/cuda/lang/__init__.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@
3434
prefetch_uniform,
3535
prefetch_tensor_map,
3636
)
37+
from ._stub.load_store_matrix import (
38+
load_matrix,
39+
store_matrix,
40+
)
41+
from ._enums import (
42+
MatrixLoadShape,
43+
MatrixLoadSourceFormat,
44+
MatrixStoreShape,
45+
)
3746
from ._stub.math import (
3847
add,
3948
sub,
@@ -464,4 +473,9 @@
464473
"prefetch",
465474
"prefetch_uniform",
466475
"prefetch_tensor_map",
476+
"load_matrix",
477+
"store_matrix",
478+
"MatrixLoadShape",
479+
"MatrixLoadSourceFormat",
480+
"MatrixStoreShape",
467481
)

experimental/cuda-lang/src/cuda/lang/_enums.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,22 @@ class PrefetchLevel(Enum):
141141
L2 = auto()
142142

143143

144+
class MatrixLoadShape(Enum):
145+
M8N8 = "m8n8"
146+
M8N16 = "m8n16"
147+
M16N16 = "m16n16"
148+
149+
150+
class MatrixStoreShape(Enum):
151+
M8N8 = "m8n8"
152+
M16N8 = "m16n8"
153+
154+
155+
class MatrixLoadSourceFormat(Enum):
156+
B6X16_P32 = "b6x16_p32"
157+
B4X16_P64 = "b4x16_p64"
158+
159+
144160
__all__ = (
145161
"MemorySpace",
146162
"MemoryScope",
@@ -164,4 +180,7 @@ class PrefetchLevel(Enum):
164180
"BarrierReductionKind",
165181
"CachePolicy",
166182
"PrefetchLevel",
183+
"MatrixStoreShape",
184+
"MatrixLoadShape",
185+
"MatrixLoadSourceFormat",
167186
)
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# SPDX-FileCopyrightText: Copyright (c) <2026> NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
import cuda.lang._datatype as datatype
6+
from cuda.lang._enums import (
7+
MatrixLoadShape,
8+
MatrixLoadSourceFormat,
9+
MatrixStoreShape,
10+
)
11+
from cuda.lang._exception import InvalidValueError, TypeCheckingError
12+
from cuda.lang._ir.ir import Var
13+
from cuda.lang._ir.op_defs import RawNVVMIntrinsic
14+
from cuda.lang._ir.op_impl.vector_impl import (
15+
vector_getitem,
16+
vector_undef,
17+
vector_with_item,
18+
)
19+
from cuda.lang._ir.type import MemorySpace, ScalarTy, VectorTy
20+
from cuda.lang._ir.type_checking_helpers import (
21+
is_none,
22+
require_pointer_in_memory_space,
23+
)
24+
from cuda.lang._stub import load_store_matrix
25+
from cuda.tile._datatype import is_integral
26+
from cuda.tile._ir.op_impl import (
27+
ImplRegistry,
28+
require_constant_bool,
29+
require_constant_enum,
30+
require_constant_int,
31+
)
32+
from cuda.tile._ir.core_ops import strictly_typed_const
33+
from cuda.tile._ir.ir import add_operation_variadic
34+
35+
36+
_registry = ImplRegistry()
37+
impl = _registry.impl
38+
39+
40+
def matrix_impl_registry() -> ImplRegistry:
41+
return _registry
42+
43+
44+
def ldmatrix_intrinsic_name(
45+
shape: MatrixLoadShape,
46+
count: int,
47+
transpose: bool,
48+
source_format: MatrixLoadSourceFormat | None,
49+
) -> str:
50+
name = f"llvm.nvvm.ldmatrix.sync.aligned.{shape.value}.x{count}"
51+
if transpose:
52+
name += ".trans"
53+
if source_format is MatrixLoadSourceFormat.B6X16_P32:
54+
return name + ".b8x16.b6x16_p32"
55+
if source_format is MatrixLoadSourceFormat.B4X16_P64:
56+
return name + ".b8x16.b4x16_p64"
57+
return name + (".b16" if shape is MatrixLoadShape.M8N8 else ".b8")
58+
59+
60+
@impl(load_store_matrix.load_matrix)
61+
def load_matrix_impl(
62+
src: Var,
63+
shape: Var,
64+
count: Var,
65+
transpose: Var,
66+
source_format: Var,
67+
) -> Var:
68+
require_pointer_in_memory_space(src, (MemorySpace.SHARED,))
69+
shape_value = require_constant_enum(shape, MatrixLoadShape)
70+
count_value = require_constant_int(count)
71+
transpose_value = require_constant_bool(transpose)
72+
source_format_value = (
73+
None
74+
if is_none(source_format)
75+
else require_constant_enum(source_format, MatrixLoadSourceFormat)
76+
)
77+
if count_value not in (1, 2, 4):
78+
raise InvalidValueError("count must be 1, 2, or 4")
79+
register_count = count_value * (
80+
2 if shape_value is MatrixLoadShape.M16N16 else 1
81+
)
82+
register_type = ScalarTy(datatype.int32)
83+
84+
name = f"llvm.nvvm.ldmatrix.sync.aligned.{shape_value.value}.x{count_value}"
85+
86+
if transpose_value:
87+
name += ".trans"
88+
89+
if source_format_value is MatrixLoadSourceFormat.B6X16_P32:
90+
name += ".b8x16.b6x16_p32"
91+
elif source_format_value is MatrixLoadSourceFormat.B4X16_P64:
92+
name += ".b8x16.b4x16_p64"
93+
else:
94+
name += ".b16" if shape_value is MatrixLoadShape.M8N8 else ".b8"
95+
96+
registers = add_operation_variadic(
97+
RawNVVMIntrinsic,
98+
(register_type,) * register_count,
99+
intrinsic=name,
100+
operands_=(src,),
101+
)
102+
if register_count == 1:
103+
return registers[0]
104+
105+
result = vector_undef(VectorTy(datatype.int32, register_count))
106+
for index, register in enumerate(registers):
107+
result = vector_with_item(result, index, register)
108+
return result
109+
110+
111+
def _store_register_count(values: Var) -> int:
112+
value_type = values.get_type()
113+
match value_type:
114+
case ScalarTy() as st:
115+
dtype = st.dtype
116+
count = 1
117+
case VectorTy() as vt:
118+
dtype = vt.element_dtype
119+
count = vt.length
120+
case _:
121+
raise TypeCheckingError(
122+
"Expected a scalar or vector of 32-bit integers"
123+
)
124+
125+
if not is_integral(dtype) or dtype.bitwidth != 32:
126+
# TODO: is this too restrictive? should we bitcast if the operand is
127+
# not integral but is 32 bits wide?
128+
raise TypeCheckingError(
129+
"Expected a scalar or vector of 32-bit integers, "
130+
f"but got {value_type}"
131+
)
132+
if count not in (1, 2, 4):
133+
raise InvalidValueError("Matrix store register count must be 1, 2, or 4")
134+
return count
135+
136+
137+
@impl(load_store_matrix.store_matrix)
138+
def store_matrix_impl(
139+
dst: Var,
140+
values: Var,
141+
shape: Var,
142+
transpose: Var,
143+
) -> None:
144+
require_pointer_in_memory_space(dst, (MemorySpace.SHARED,))
145+
shape_value = require_constant_enum(shape, MatrixStoreShape)
146+
transpose_value = require_constant_bool(transpose)
147+
register_count = _store_register_count(values)
148+
if shape_value is MatrixStoreShape.M16N8 and not transpose_value:
149+
raise InvalidValueError("M16N8 requires transpose=True")
150+
151+
if register_count == 1:
152+
registers = (values,)
153+
else:
154+
index_type = ScalarTy(datatype.int32)
155+
registers = tuple(
156+
vector_getitem(values, strictly_typed_const(index, index_type))
157+
for index in range(register_count)
158+
)
159+
160+
name = f"llvm.nvvm.stmatrix.sync.aligned.{shape_value.value}.x{register_count}"
161+
if transpose_value:
162+
name += ".trans"
163+
name += ".b16" if shape_value is MatrixStoreShape.M8N8 else ".b8"
164+
165+
add_operation_variadic(
166+
RawNVVMIntrinsic,
167+
(),
168+
intrinsic=name,
169+
operands_=(dst, *registers),
170+
)

experimental/cuda-lang/src/cuda/lang/_ir/ops.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@
157157
from .op_impl.barrier_impl import barrier_impl_registry
158158
from .op_impl.mbarrier_impl import mbarrier_impl_registry
159159
from .op_impl.inline_ptx_impl import inline_ptx_impl_registry
160+
from .op_impl.matrix_impl import matrix_impl_registry
160161

161162
cuda_lang_impl_registry = ImplRegistry()
162163
cuda_lang_impl_registry.update(core_impl_registry())
@@ -174,6 +175,7 @@
174175
cuda_lang_impl_registry.update(copy_async_impl_registry())
175176
cuda_lang_impl_registry.update(barrier_impl_registry())
176177
cuda_lang_impl_registry.update(mbarrier_impl_registry())
178+
cuda_lang_impl_registry.update(matrix_impl_registry())
177179

178180
impl = cuda_lang_impl_registry.impl
179181

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# SPDX-FileCopyrightText: Copyright (c) <2026> NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
from .._enums import (
6+
MatrixStoreShape,
7+
MatrixLoadShape,
8+
MatrixLoadSourceFormat,
9+
)
10+
from cuda.lang._execution import stub
11+
from typing import Literal
12+
13+
14+
@stub()
15+
def load_matrix(
16+
src,
17+
/,
18+
*,
19+
shape: MatrixLoadShape,
20+
count: Literal[1, 2, 4] = 1,
21+
transpose: bool = False,
22+
source_format: MatrixLoadSourceFormat | None = None,
23+
):
24+
"""
25+
Collectively load one or more matrices from shared memory for mma instruction
26+
27+
Args:
28+
src: Pointer to a matrix row in shared memory.
29+
shape: Shape and element size.
30+
count: Number of matrices to load.
31+
transpose: False loads the matrix in row-major order, True loads in
32+
column-major order.
33+
source_format: Packed source format.
34+
35+
Returns:
36+
Scalar or vector of 32 bit integers depending on ``count``.
37+
"""
38+
...
39+
40+
41+
@stub()
42+
def store_matrix(
43+
dst,
44+
values,
45+
/,
46+
*,
47+
shape: MatrixStoreShape,
48+
transpose: bool = False,
49+
):
50+
"""
51+
Collectively store one or more matrices to shared memory.
52+
53+
Args:
54+
dst: Pointer to a matrix row in shared memory.
55+
values: One 32-bit scalar or a ``Vector`` of 1, 2, or 4 32-bit values.
56+
The number of values selects the number of matrices.
57+
shape: Matrix shape.
58+
transpose: False stores the matrix in row-major order, True stores in
59+
column-major order.
60+
"""
61+
...

0 commit comments

Comments
 (0)