This is an automated email from the ASF dual-hosted git repository.

tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/main by this push:
     new ed5e2fed3f [FIX][TIRx][CUDA] Support SM100 weight-stationary B 
collectors (#20329)
ed5e2fed3f is described below

commit ed5e2fed3f32030d9407e72751550b26653c5fac
Author: Hongyi Jin <[email protected]>
AuthorDate: Sun Sep 13 09:44:50 2026 -0400

    [FIX][TIRx][CUDA] Support SM100 weight-stationary B collectors (#20329)
    
    The SM100 `tcgen05.mma.ws` SS/TS entries omit the collector-B modifier
    slot, preventing callers from expressing weight-stationary B reuse
    through the PTX namespace. Add the existing optional B0–B3
    fill/use/lastuse/discard domain to both entries, preserving calls that
    omit the qualifier.
    
    Also reject modifier-domain and operand-count mismatches during code
    generation, before stale table/codegen layouts can emit malformed CUDA
    helpers. Add dispatch and parser round-trip regressions, SM100
    certification for all WS variants, and stale-layout diagnostics. Make
    the test source helper accept an explicit target so the existing SM107
    collector test compiles at its required architecture.
    
    Validation with CUDA 13.4 NVCC/NVRTC and a matching worktree FFI:
    
    - PTX dialect, conversion, and address suites: 179 non-skipped cases
    passed across the full run and targeted reruns.
    - Full-table NVCC/ptxas certification: **32/32 shards passed**,
    including predicated helpers and address-offset samples at their
    declared architectures.
    - Regenerated IDE stubs: no diff needed; the tokens already exist in the
    ti16 families.
    - Changed-file pre-commit hooks and `git diff --check` passed.
    
    Closes #20328.
---
 python/tvm/backend/cuda/ptx/engine.py         | 12 ++++
 python/tvm/backend/cuda/ptx/table.py          |  2 +
 tests/python/tirx/codegen/test_ptx_dialect.py | 86 +++++++++++++++++++++++++--
 3 files changed, 95 insertions(+), 5 deletions(-)

diff --git a/python/tvm/backend/cuda/ptx/engine.py 
b/python/tvm/backend/cuda/ptx/engine.py
index ae1b2b9ad7..0b7de8a736 100644
--- a/python/tvm/backend/cuda/ptx/engine.py
+++ b/python/tvm/backend/cuda/ptx/engine.py
@@ -186,6 +186,12 @@ def _make_codegen(entry: InstructionEntry):
         preserve_dst = "keep" in flags
         tokens = [parse_str(a) for a in args[len(args) - n_slots - 1 : -1]]
         rest = list(args[: len(args) - n_slots - 1])  # operands, plus pred 
when present
+        for slot, token in zip(entry.slots, tokens, strict=True):
+            if token not in slot.choices and not (slot.optional and token == 
""):
+                raise ValueError(
+                    f"{entry.name}: invalid codegen modifier {token!r} for 
slot {slot.name!r}; "
+                    "the PTX call and registered table entry may be 
inconsistent"
+                )
         mod_map = mods(entry, tokens)
         layout = operand_layout(entry, mod_map)
         n_operands = sum(n for _, _, n in layout)
@@ -199,6 +205,12 @@ def _make_codegen(entry: InstructionEntry):
                 at[i] = pos
                 pos += 1
         n_present = pos
+        expected_args = n_present + int(predicated)
+        if len(rest) != expected_args:
+            raise ValueError(
+                f"{entry.name}: expected {expected_args} codegen operand(s), 
got {len(rest)}; "
+                "the PTX call and registered table entry may be inconsistent"
+            )
         sinks = frozenset(
             (slot.name, lane)
             for slot, i, lanes in layout
diff --git a/python/tvm/backend/cuda/ptx/table.py 
b/python/tvm/backend/cuda/ptx/table.py
index 94e0121f82..ac283dca18 100644
--- a/python/tvm/backend/cuda/ptx/table.py
+++ b/python/tvm/backend/cuda/ptx/table.py
@@ -11529,6 +11529,7 @@ _ENTRIES = [
         )
         for form in ("ss", "ts")
     ],
+    # PTX ISA 9.7.18.10.10.3: optional 
collector::b{0,1,2,3}::{fill,use,lastuse,discard}.
     *[
         InstructionEntry(  # weight-stationary: no mask vector, a zero-column 
desc
             name=f"tcgen05_mma_ws_{form}",
@@ -11538,6 +11539,7 @@ _ENTRIES = [
                 ModifierSlot("ws", ("ws",)),
                 ModifierSlot("cta_group", ("cta_group::1",)),
                 ModifierSlot("kind", ("kind::f16", "kind::tf32", 
"kind::f8f6f4", "kind::i8")),
+                ModifierSlot("collector_b", _TCGEN05_WS_COLLECTOR_B, 
optional=True),
             ),
             cert_arch="sm_100a",
             operands=(
diff --git a/tests/python/tirx/codegen/test_ptx_dialect.py 
b/tests/python/tirx/codegen/test_ptx_dialect.py
index 0e4737292f..9db4666189 100644
--- a/tests/python/tirx/codegen/test_ptx_dialect.py
+++ b/tests/python/tirx/codegen/test_ptx_dialect.py
@@ -34,9 +34,9 @@ TARGET = tvm.target.Target("cuda")
 requires_nvcc = pytest.mark.skipif(shutil.which("nvcc") is None, reason="nvcc 
not available")
 
 
-def _cuda_source(func) -> str:
-    with TARGET:
-        mod = tvm.compile(tvm.IRModule({"main": func}), target=TARGET, 
tir_pipeline="tirx")
+def _cuda_source(func, target=TARGET) -> str:
+    with target:
+        mod = tvm.compile(tvm.IRModule({"main": func}), target=target, 
tir_pipeline="tirx")
     return mod.mod.imports[0].inspect_source("cuda")
 
 
@@ -2340,6 +2340,80 @@ def 
test_ptx_wgmma_integer_shape_domains_follow_concrete_syntax():
         assert "m64n256k256" in b1_shapes
 
 
[email protected]("form", ("ss", "ts"))
[email protected]("collector", ("", "b0::fill", "b1::use", 
"b2::lastuse", "b3::discard"))
+def test_ptx_tcgen05_mma_ws_collector_dispatch(form, collector):
+    opcode = "tcgen05.mma.ws.cta_group::1.kind::f16"
+    if collector:
+        opcode += f".collector::{collector}"
+    a_dtype = "uint64" if form == "ss" else "uint32"
+
+    @T.prim_func
+    def kernel():
+        T.device_entry()
+        T.cta_id([1])
+        T.thread_id([32])
+        T.ptx[opcode](
+            T.uint32(0),
+            T.cast(0, a_dtype),
+            T.uint64(0),
+            T.uint32(0),
+            T.ptx.pred(T.uint32(0)),
+            T.uint64(0),
+        )
+
+    src = _cuda_source(kernel, tvm.target.Target({"kind": "cuda", "arch": 
"sm_100a"}))
+    a_operand = "%1" if form == "ss" else "[%1]"
+    assert f"{opcode} [%0], {a_operand}, %2, %3, ps0, %5;" in src
+    tvm.ir.assert_structural_equal(kernel, 
tvm.script.from_source(kernel.script()))
+
+
+@requires_nvcc
+def test_ptx_tcgen05_mma_ws_collectors_certify_sm100a():
+    """All WS kinds, B buffers/operations, and predication assemble on 
SM100a."""
+    from tvm.backend.cuda.ptx.render import render_variant
+    from tvm.backend.cuda.ptx.table import TABLE, renderings
+
+    by_arch = {}
+    for form in ("ss", "ts"):
+        entry = TABLE[f"tcgen05_mma_ws_{form}"]
+        for rendering in renderings(entry):
+            _, helper, source = render_variant(entry, 
*_as_render_args(rendering))
+            _append_certification(by_arch, "sm_100a", helper, source)
+    _assert_certifications_ok(by_arch)
+
+
[email protected]("mismatch", ("modifier", "operand"))
+def test_ptx_codegen_rejects_stale_table_layout(mismatch):
+    """A traced call must not silently feed modifier strings to an old 
helper."""
+    from dataclasses import replace
+
+    from tvm.backend.cuda.ptx.engine import PTXNamespace, _make_codegen
+    from tvm.backend.cuda.ptx.table import TABLE, ModifierSlot, OperandSlot
+
+    entry = TABLE["tcgen05_mma_ws_ss"]
+    if mismatch == "modifier":
+        changed = replace(entry, slots=(*entry.slots, ModifierSlot("extra", 
("extra",))))
+        opcode = "tcgen05.mma.ws.cta_group::1.kind::f16.extra"
+        extra = ()
+    else:
+        changed = replace(entry, operands=(*entry.operands, 
OperandSlot("extra", dtype="u64")))
+        opcode = "tcgen05.mma.ws.cta_group::1.kind::f16"
+        extra = (T.uint64(0),)
+    namespace = PTXNamespace({changed.name: changed})
+    call = namespace[opcode](
+        T.uint32(0),
+        T.uint64(0),
+        T.uint64(0),
+        T.uint32(0),
+        namespace.pred(T.uint32(0)),
+        T.uint64(0),
+        *extra,
+    )
+    with pytest.raises(ValueError, match="PTX call and registered table entry 
may be inconsistent"):
+        _make_codegen(entry)(*call.args)
+
+
 @requires_nvcc
 def test_ptx_tcgen05_mma_block_size_form():
     @T.prim_func
@@ -2405,7 +2479,9 @@ def test_ptx_tcgen05_mma_block_size_collector_form():
             ](tmem, tmem, desc, idesc, tmem, tmem, T.ptx.pred(flag))
         A[tx] = A[tx]
 
-    collector_src = _cuda_source(sm107_collector_kernel)
+    collector_src = _cuda_source(
+        sm107_collector_kernel, tvm.target.Target({"kind": "cuda", "arch": 
"sm_107f"})
+    )
     ss_collector_opcode = (
         "tcgen05.mma.cta_group::1.kind::mxf4nvf4.block_scale.block16"
         ".collector::a::discard.collector::b::fill"
@@ -4144,7 +4220,7 @@ def test_ptx_all_variants_render_unique():
             _, helper, _ = render_variant(entry, *args, 
addr_offsets=addr_offsets)
             assert helper not in names, f"address-offset helper name 
collision: {helper}"
             names.add(helper)
-    assert total == 762050  # update when the table grows or a ptxas gap 
narrows it
+    assert total == 762178  # update when the table grows or a ptxas gap 
narrows it
 
 
 def test_ptx_no_instruction_registered_twice():

Reply via email to