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

tlopex 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 a540271e61 [DLight][CUDA] Fix undefined TX in GEMV broadcast epilogue 
(#19970)
a540271e61 is described below

commit a540271e619bc6bf3f09079fd4634336fab8cec1
Author: Nanmur <[email protected]>
AuthorDate: Tue Jul 14 02:43:12 2026 +0800

    [DLight][CUDA] Fix undefined TX in GEMV broadcast epilogue (#19970)
    
    This PR fixes an undefined `TX` reference in the DLight GPU GEMV
    inner-reduction schedule.
    
    The broadcast epilogue path splits fused epilogue loops and binds the
    inner loop to `threadIdx.x`, but it referenced `TX`, which is not
    defined in the surrounding scope or passed into the helper. This PR uses
    the existing `TR` tile factor, which is already used for the
    `threadIdx.x` direction in this schedule.
    
    A regression test is added to cover the GEMV broadcast epilogue path.
    
    Fixes #19969
    
    Tests:
    - `python -m pytest tests/python/s_tir/dlight/test_gpu_gemv.py -q`
    - `python -m pytest tests/python/s_tir/dlight/test_gpu_low_batch_gemv.py
    -q`
    - `python -m pytest tests/python/s_tir/dlight/test_gpu_reduction.py -q`
    
    Note: The branch is based on `apache/tvm:main`. Local main-branch test
    execution on this Windows machine could not be completed because the
    available compiled TVM library is from a v0.25 build and does not match
    the latest Python sources; the same tests passed in the matching local
    v0.25 environment before rebasing the patch to main.
---
 python/tvm/s_tir/dlight/gpu/gemv.py                |  2 +-
 python/tvm/s_tir/dlight/gpu/low_batch_gemv.py      |  2 +-
 tests/python/s_tir/dlight/test_gpu_gemv.py         | 43 +++++++++++++++++++
 .../python/s_tir/dlight/test_gpu_low_batch_gemv.py | 49 ++++++++++++++++++++++
 4 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/python/tvm/s_tir/dlight/gpu/gemv.py 
b/python/tvm/s_tir/dlight/gpu/gemv.py
index 0400819974..c893c3449a 100644
--- a/python/tvm/s_tir/dlight/gpu/gemv.py
+++ b/python/tvm/s_tir/dlight/gpu/gemv.py
@@ -288,7 +288,7 @@ class GEMV(GPUScheduleRule):
                     sch.reverse_compute_at(epilogue, bx)
                     sch.set_scope(block, 0, "shared")
                     _, _, *s = sch.get_loops(epilogue)  # pylint: 
disable=invalid-name
-                    _, tx = sch.split(sch.fuse(*s), factors=[None, TX])
+                    _, tx = sch.split(sch.fuse(*s), factors=[None, TR])
                     sch.bind(tx, "threadIdx.x")
                 else:
                     sch.reverse_compute_at(epilogue, bx, 
preserve_unit_loops=True)
diff --git a/python/tvm/s_tir/dlight/gpu/low_batch_gemv.py 
b/python/tvm/s_tir/dlight/gpu/low_batch_gemv.py
index 0e6de05325..e854ec1e26 100644
--- a/python/tvm/s_tir/dlight/gpu/low_batch_gemv.py
+++ b/python/tvm/s_tir/dlight/gpu/low_batch_gemv.py
@@ -492,7 +492,7 @@ class LowBatchGEMV(GPUScheduleRule):
                     sch.reverse_compute_at(epilogue, bx)
                     sch.set_scope(block, 0, "shared")
                     _, _, _, *s = sch.get_loops(epilogue)  # pylint: 
disable=invalid-name
-                    _, tx = sch.split(sch.fuse(*s), factors=[None, TX])
+                    _, tx = sch.split(sch.fuse(*s), factors=[None, TS])
                     sch.bind(tx, TAG_S)
                 else:
                     sch.reverse_compute_at(epilogue, bx, 
preserve_unit_loops=True)
diff --git a/tests/python/s_tir/dlight/test_gpu_gemv.py 
b/tests/python/s_tir/dlight/test_gpu_gemv.py
index da62ffb1f4..14adab22ea 100644
--- a/tests/python/s_tir/dlight/test_gpu_gemv.py
+++ b/tests/python/s_tir/dlight/test_gpu_gemv.py
@@ -1087,5 +1087,48 @@ def 
test_gemv_cuda_target_without_max_shared_memory_per_block():
     assert mod["main"].attrs["tirx.is_scheduled"] == 1
 
 
+def test_gemv_broadcast_epilogue():
+    # fmt: off
+    @T.prim_func(private=True, s_tir=True)
+    def before(
+        A: T.Buffer((1, 32, 1, 128), "float16"),
+        p_B: T.handle,
+        p_C: T.handle,
+    ):
+        T.func_attr({"tirx.noalias": True})
+        n = T.int32()
+        B = T.match_buffer(p_B, (1, 32, n, 128), "float16")
+        C = T.match_buffer(p_C, (1, 32, 2, 3, n), "float32")
+        C_temp = T.sblock_alloc_buffer((1, 32, 1, n), "float16")
+        for i0, i1, i2, i3, k in T.grid(1, 32, 1, n, 128):
+            with T.sblock("NT_matmul"):
+                v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, 
i2, i3, k])
+                T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i0, v_i1, v_i3, v_k])
+                T.writes(C_temp[v_i0, v_i1, v_i2, v_i3])
+                with T.init():
+                    C_temp[v_i0, v_i1, v_i2, v_i3] = T.float16(0)
+                C_temp[v_i0, v_i1, v_i2, v_i3] = C_temp[
+                    v_i0, v_i1, v_i2, v_i3
+                ] + A[v_i0, v_i1, v_i2, v_k] * B[v_i0, v_i1, v_i3, v_k]
+        for i0, i1, i2, i3, i4 in T.grid(1, 32, 2, 3, n):
+            with T.sblock("broadcast_epilogue"):
+                v_i0, v_i1, v_i2, v_i3, v_i4 = T.axis.remap(
+                    "SSSSS", [i0, i1, i2, i3, i4]
+                )
+                T.reads(C_temp[v_i0, v_i1, 0, v_i4])
+                T.writes(C[v_i0, v_i1, v_i2, v_i3, v_i4])
+                C[v_i0, v_i1, v_i2, v_i3, v_i4] = T.Cast(
+                    "float32", C_temp[v_i0, v_i1, 0, v_i4]
+                )
+
+    # fmt: on
+
+    mod = tvm.IRModule({"main": before})
+    with Target("nvidia/geforce-rtx-3090-ti"):
+        mod = dl.ApplyDefaultSchedule(dl.gpu.GEMV())(mod)
+
+    assert mod["main"].attrs["tirx.is_scheduled"] == 1
+
+
 if __name__ == "__main__":
     tvm.testing.main()
diff --git a/tests/python/s_tir/dlight/test_gpu_low_batch_gemv.py 
b/tests/python/s_tir/dlight/test_gpu_low_batch_gemv.py
index 61f459c8d0..6f75191e50 100644
--- a/tests/python/s_tir/dlight/test_gpu_low_batch_gemv.py
+++ b/tests/python/s_tir/dlight/test_gpu_low_batch_gemv.py
@@ -17,9 +17,11 @@
 # pylint: disable=missing-docstring
 # ruff: noqa: E501
 
+from unittest import mock
 
 import tvm.testing
 from tvm.s_tir import dlight as dl
+from tvm.s_tir.dlight.gpu import low_batch_gemv
 from tvm.script import tirx as T
 from tvm.target import Target
 
@@ -557,5 +559,52 @@ def 
test_low_batch_gemv_cuda_target_without_max_shared_memory_per_block():
     assert mod["main"].attrs["tirx.is_scheduled"] == 1
 
 
+def test_low_batch_gemv_broadcast_epilogue():
+    # fmt: off
+    @T.prim_func(private=True, s_tir=True)
+    def before(
+        var_A: T.handle,
+        B: T.Buffer((T.int64(128), T.int64(128)), "float16"),
+        var_C: T.handle,
+    ):
+        T.func_attr({"tirx.noalias": True})
+        batch_size = T.int64()
+        A = T.match_buffer(var_A, (T.int64(1), batch_size, T.int64(1), 
T.int64(128)), "float16")
+        C = T.match_buffer(var_C, (T.int64(1), batch_size, T.int64(2), 
T.int64(3), T.int64(128)), "float32")
+        C_temp = T.sblock_alloc_buffer((T.int64(1), batch_size, T.int64(1), 
T.int64(128)), "float16")
+        for i0, i1, i2, i3, k in T.grid(
+            T.int64(1), batch_size, T.int64(1), T.int64(128), T.int64(128)
+        ):
+            with T.sblock("NT_matmul"):
+                v_i0, v_i1, v_i2, v_i3, v_k = T.axis.remap("SSSSR", [i0, i1, 
i2, i3, k])
+                T.reads(A[v_i0, v_i1, v_i2, v_k], B[v_i3, v_k])
+                T.writes(C_temp[v_i0, v_i1, v_i2, v_i3])
+                with T.init():
+                    C_temp[v_i0, v_i1, v_i2, v_i3] = T.float16(0)
+                C_temp[v_i0, v_i1, v_i2, v_i3] = (
+                    C_temp[v_i0, v_i1, v_i2, v_i3]
+                    + A[v_i0, v_i1, v_i2, v_k] * B[v_i3, v_k]
+                )
+        for i0, i1, i2, i3, i4 in T.grid(
+            T.int64(1), batch_size, T.int64(2), T.int64(3), T.int64(128)
+        ):
+            with T.sblock("broadcast_epilogue"):
+                v_i0, v_i1, v_i2, v_i3, v_i4 = T.axis.remap("SSSSS", [i0, i1, 
i2, i3, i4])
+                T.reads(C_temp[v_i0, v_i1, T.int64(0), v_i4])
+                T.writes(C[v_i0, v_i1, v_i2, v_i3, v_i4])
+                C[v_i0, v_i1, v_i2, v_i3, v_i4] = T.Cast(
+                    "float32", C_temp[v_i0, v_i1, T.int64(0), v_i4]
+                )
+    # fmt: on
+
+    mod = tvm.IRModule({"main": before})
+    with mock.patch.object(low_batch_gemv, "is_broadcast_epilogue", 
return_value=True) as check:
+        with Target("nvidia/geforce-rtx-3090-ti"):
+            mod = dl.ApplyDefaultSchedule(dl.gpu.LowBatchGEMV(4))(mod)
+
+    check.assert_called_once()
+    assert mod["main"].attrs["tirx.is_scheduled"] == 1
+
+
 if __name__ == "__main__":
     tvm.testing.main()

Reply via email to