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

spectrometerHBH 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 448041da62 [TIRx][CUDA] Allow launch bounds with required block size 
(#20226)
448041da62 is described below

commit 448041da62e7fedfa077d462f9e7c3f841567602
Author: Bohan Hou <[email protected]>
AuthorDate: Sat Aug 29 10:33:49 2026 -0400

    [TIRx][CUDA] Allow launch bounds with required block size (#20226)
    
    This change allows `tirx.required_block_size` to be combined with CUDA
    launch-bounds attributes while preserving the existing incompatibility
    with `tirx.max_registers`.
    
    The combination is needed for kernels that require exact block/cluster
    dimensions and also need a minimum blocks-per-SM launch bound. The CUDA
    code generator now emits both attributes, and SplitHostDevice retains
    the supported combination.
    
    Tests:
    - `python -m pytest -q
    tests/python/tirx-transform/test_tir_transform_split_host_device.py
    tests/python/tirx/codegen/test_codegen_cuda.py` (225 passed)
    - `pre-commit run --files src/backend/cuda/codegen/codegen_cuda.cc
    src/tirx/transform/split_host_device.cc
    tests/python/tirx-transform/test_tir_transform_split_host_device.py
    tests/python/tirx/codegen/test_codegen_cuda.py`
---
 src/backend/cuda/codegen/codegen_cuda.cc                   | 14 ++++++++------
 src/tirx/transform/split_host_device.cc                    |  7 ++-----
 .../tirx-transform/test_tir_transform_split_host_device.py | 12 +++++++-----
 tests/python/tirx/codegen/test_codegen_cuda.py             | 14 ++++++++------
 4 files changed, 25 insertions(+), 22 deletions(-)

diff --git a/src/backend/cuda/codegen/codegen_cuda.cc 
b/src/backend/cuda/codegen/codegen_cuda.cc
index ef5f13c0a5..be2bfe9bae 100644
--- a/src/backend/cuda/codegen/codegen_cuda.cc
+++ b/src/backend/cuda/codegen/codegen_cuda.cc
@@ -247,11 +247,8 @@ void CodeGenCUDA::PrintExtraAttrs(const PrimFunc& f, 
std::ostream& os) {
   auto required_block_size = 
f->GetAttr<int64_t>(tirx::attr::kRequiredBlockSize);
   if (required_block_size.has_value()) {
     TVM_FFI_ICHECK_EQ(required_block_size.value(), 1);
-    TVM_FFI_ICHECK(!max_registers.has_value() &&
-                   
!f->GetAttr<int64_t>(tirx::attr::kLaunchBoundsMinBlocksPerSM).has_value() &&
-                   
!f->GetAttr<int64_t>(tirx::attr::kLaunchBoundsMaxBlocksPerCluster).has_value())
-        << tirx::attr::kRequiredBlockSize
-        << " cannot be combined with CUDA launch bounds or maximum registers";
+    TVM_FFI_ICHECK(!max_registers.has_value())
+        << tirx::attr::kRequiredBlockSize << " cannot be combined with maximum 
registers";
     const auto* tx = extractor.threadIdx_x_ext.as<IntImmNode>();
     const auto* ty = extractor.threadIdx_y_ext.as<IntImmNode>();
     const auto* tz = extractor.threadIdx_z_ext.as<IntImmNode>();
@@ -262,7 +259,12 @@ void CodeGenCUDA::PrintExtraAttrs(const PrimFunc& f, 
std::ostream& os) {
         << tirx::attr::kRequiredBlockSize << " requires static thread and 
cluster dimensions";
     os << " __block_size__((" << tx->value << ", " << ty->value << ", " << 
tz->value << "), ("
        << cx->value << ", " << cy->value << ", " << cz->value << "))";
-    return;
+    if 
(!f->GetAttr<int64_t>(tirx::attr::kLaunchBoundsMinBlocksPerSM).has_value()) {
+      
TVM_FFI_ICHECK(!f->GetAttr<int64_t>(tirx::attr::kLaunchBoundsMaxBlocksPerCluster).has_value())
+          << tirx::attr::kLaunchBoundsMaxBlocksPerCluster << " requires "
+          << tirx::attr::kLaunchBoundsMinBlocksPerSM;
+      return;
+    }
   }
   if (max_registers.has_value()) {
     TVM_FFI_ICHECK_GT(max_registers.value(), 0);
diff --git a/src/tirx/transform/split_host_device.cc 
b/src/tirx/transform/split_host_device.cc
index afb98b1690..8aba699d1f 100644
--- a/src/tirx/transform/split_host_device.cc
+++ b/src/tirx/transform/split_host_device.cc
@@ -99,11 +99,8 @@ class LaunchBoundsAttrExtractor : public StmtMutator {
     TVM_FFI_ICHECK(!max_registers_.has_value() ||
                    (!min_blocks_per_sm_.has_value() && 
!max_blocks_per_cluster_.has_value()))
         << tirx::attr::kMaxRegisters << " cannot be combined with CUDA launch 
bounds";
-    TVM_FFI_ICHECK(!required_block_size_.has_value() ||
-                   (!min_blocks_per_sm_.has_value() && 
!max_blocks_per_cluster_.has_value() &&
-                    !max_registers_.has_value()))
-        << tirx::attr::kRequiredBlockSize
-        << " cannot be combined with CUDA launch bounds or maximum registers";
+    TVM_FFI_ICHECK(!required_block_size_.has_value() || 
!max_registers_.has_value())
+        << tirx::attr::kRequiredBlockSize << " cannot be combined with maximum 
registers";
     return result;
   }
 
diff --git 
a/tests/python/tirx-transform/test_tir_transform_split_host_device.py 
b/tests/python/tirx-transform/test_tir_transform_split_host_device.py
index ddc11bb1d4..2990efc725 100644
--- a/tests/python/tirx-transform/test_tir_transform_split_host_device.py
+++ b/tests/python/tirx-transform/test_tir_transform_split_host_device.py
@@ -437,7 +437,7 @@ def test_cuda_launch_preserves_flag_metadata():
     assert int(launch.args[-1]) == 16
 
 
-def test_cuda_required_block_size_becomes_flag_only_launch_metadata():
+def test_cuda_required_block_size_coexists_with_launch_bounds():
     @I.ir_module
     class Before:
         @T.prim_func(s_tir=True)
@@ -445,14 +445,16 @@ def 
test_cuda_required_block_size_becomes_flag_only_launch_metadata():
             T.func_attr({"target": T.target("cuda", host="llvm")})
             T.attr(T.target("cuda"), "target", 0)
             T.attr(0, "tirx.required_block_size", 1)
-            bx = T.launch_thread("blockIdx.x", 4)
-            tx = T.launch_thread("threadIdx.x", 128)
-            if tx == 0:
-                A[bx] = 0.0
+            with T.attr(0, "tirx.launch_bounds_min_blocks_per_sm", 1):
+                bx = T.launch_thread("blockIdx.x", 4)
+                tx = T.launch_thread("threadIdx.x", 128)
+                if tx == 0:
+                    A[bx] = 0.0
 
     after = tvm.tirx.transform.SplitHostDevice()(Before)
     kernel = after["main_kernel"]
     assert int(kernel.attrs["tirx.required_block_size"]) == 1
+    assert int(kernel.attrs["tirx.launch_bounds_min_blocks_per_sm"]) == 1
     assert list(kernel.attrs["tirx.kernel_launch_params"]) == [
         "blockIdx.x",
         "threadIdx.x",
diff --git a/tests/python/tirx/codegen/test_codegen_cuda.py 
b/tests/python/tirx/codegen/test_codegen_cuda.py
index 1a79db2b63..46cec9d303 100644
--- a/tests/python/tirx/codegen/test_codegen_cuda.py
+++ b/tests/python/tirx/codegen/test_codegen_cuda.py
@@ -287,7 +287,7 @@ def test_tirx_required_block_size_emits_cuda_block_size():
     assert "tirx.required_block_size" not in src
 
 
-def test_tirx_required_block_size_rejects_launch_controls():
+def test_tirx_required_block_size_emits_launch_bounds_when_requested():
     @T.prim_func
     def main(A: T.Buffer((4,), "int32")):
         T.device_entry()
@@ -302,11 +302,13 @@ def 
test_tirx_required_block_size_rejects_launch_controls():
         if tx == 0:
             A[bx] = A[bx] + 1
 
-    with pytest.raises(
-        tvm.error.InternalError,
-        match="cannot be combined with CUDA launch bounds or maximum 
registers",
-    ):
-        _get_source(main)
+    src, _ = _get_source(main)
+    assert (
+        'extern "C" __global__ void __block_size__((128, 1, 1), (1, 1, 1)) '
+        "__launch_bounds__(128, 1) main_kernel" in src
+    )
+    assert "tirx.required_block_size" not in src
+    assert "tirx.launch_bounds_min_blocks_per_sm" not in src
 
 
 def test_tirx_cuda_kernel_return_zero_codegen_is_void_early_return():

Reply via email to