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 cb80add495 fix(lower-tirx): restore CUDA lowering with native IR 
builders (#20620)
cb80add495 is described below

commit cb80add495bb4ebefee0d918c14c9408f1704ea2
Author: Bohan Hou <[email protected]>
AuthorDate: Thu Oct 8 18:26:11 2026 -0400

    fix(lower-tirx): restore CUDA lowering with native IR builders (#20620)
    
    The native TIRx builder migration leaves CUDA tile primitives using
    implicit host branches, TensorMap calls without a native attribute
    schema, and TMEM types whose address metadata references replaced
    variables. This change restores lowering against the native IR
    contracts.
    
    - Mark compile-time control flow explicitly and keep constant TMEM
    coordinates visible to address folding.
    - Register TensorMap attributes and defaults, and remap `allocated_addr`
    when rebuilding tensor types.
    - Allow dispatch fragments to capture enclosing thread bindings while
    retaining validation of complete functions; reject distributed tensors
    in scalar copy fallback and normalize single-axis DSMEM loops.
    - Add regressions for TensorMap attributes, TMEM metadata, and fallback
    rejection. Update instruction/IR assertions and remove three obsolete
    xfail marks.
    
    Integration validation uses the companion [tirx-kernels
    migration](https://github.com/mlc-ai/TIRx-kernels/pull/6), which updates
    the registered kernels to the native builder API.
    
    Validation on TVM `e17fcc98f9` plus this change:
    
    - Incremental TVM build and pre-commit: passed.
    - `python -m tirx_kernels.bench_suite --check-imports`: passed (274
    workloads, 93 kernels).
    - `python -m tirx_kernels.registry --strict`: passed.
    - `python -m pytest tests/python/tirx/ -n 16`: 2926 passed, 308 skipped,
    zero failures/errors/XPASS in 151.77 seconds on physical GPUs 0-4.
    
    The skips retain the existing reference-dependency, toolchain, and
    unsupported-configuration conditions.
---
 python/tvm/backend/cuda/lang/clc.py                |  4 +-
 python/tvm/backend/cuda/lang/pipeline.py           | 14 ++--
 python/tvm/backend/cuda/lang/tile_scheduler.py     |  2 +-
 python/tvm/backend/cuda/tile_primitive/common.py   | 14 ++--
 .../backend/cuda/tile_primitive/copy/fallback.py   | 19 ++++-
 .../cuda/tile_primitive/copy/ld_stmatrix.py        |  2 +-
 .../cuda/tile_primitive/copy/vec_auto_gmem_smem.py |  2 +-
 .../cuda/tile_primitive/copy/vec_auto_reg.py       |  6 +-
 .../backend/cuda/tile_primitive/copy/vec_forced.py |  4 +-
 .../cuda/tile_primitive/copy_async/dsmem.py        |  2 +
 .../cuda/tile_primitive/elementwise/_common.py     |  6 +-
 .../backend/cuda/tile_primitive/elementwise/reg.py |  7 +-
 .../cuda/tile_primitive/elementwise/smem.py        |  2 +-
 .../cuda/tile_primitive/gemm/mma_m16n8k_.py        |  2 +-
 .../cuda/tile_primitive/gemm_async/tcgen05.py      | 52 ++++---------
 .../permute_layout/warp_xor_swizzle.py             |  4 +-
 .../backend/cuda/tile_primitive/reduction/local.py | 14 ++--
 .../cuda/tile_primitive/reduction/shared.py        | 14 ++--
 src/tirx/op/builtin.cc                             | 13 ++--
 src/tirx/transform/lower_tirx_cleanup.cc           |  8 ++
 tests/python/tirx/codegen/test_codegen_ampere.py   | 12 +--
 tests/python/tirx/codegen/test_codegen_cuda.py     | 23 ++++--
 tests/python/tirx/codegen/test_codegen_hopper.py   | 14 +++-
 tests/python/tirx/codegen/test_codegen_nvshmem.py  |  2 +-
 tests/python/tirx/codegen/test_ptx_ld_st_ops.py    |  6 +-
 .../tile_primitive/cuda/copy/test_gmem_smem.py     |  8 +-
 .../tile_primitive/cuda/copy/test_ld_stmatrix.py   |  8 +-
 .../operator/tile_primitive/cuda/copy/test_reg.py  | 67 +++++++++++++---
 .../cuda/copy_async/test_tcgen05_cp.py             |  6 +-
 .../cuda/copy_async/test_tcgen05_ldst.py           |  2 +-
 .../tile_primitive/cuda/elementwise/test_binary.py | 80 +++++++++----------
 .../tile_primitive/cuda/elementwise/test_unary.py  | 72 ++++++++---------
 .../cuda/gemm/test_gemm_mma_m16n8k_.py             | 57 +++++++++-----
 .../cuda/gemm_async/test_gemm_async.py             |  2 +-
 .../cuda/reduction/test_reduction.py               | 90 +++++++++++-----------
 tests/python/tirx/test_op_namespace_cleanup.py     | 30 ++++++++
 .../tirx/transform/test_transform_lower_tirx.py    | 30 ++++++++
 37 files changed, 426 insertions(+), 274 deletions(-)

diff --git a/python/tvm/backend/cuda/lang/clc.py 
b/python/tvm/backend/cuda/lang/clc.py
index 670bc0c503..1b958cd240 100644
--- a/python/tvm/backend/cuda/lang/clc.py
+++ b/python/tvm/backend/cuda/lang/clc.py
@@ -39,7 +39,9 @@ def query_cancel_first_ctaid_x(first_ctaid_x, handle, *, 
use_ld_acquire=True):
     response = T.local_scalar("uint128")
     canceled = T.local_scalar("uint32")
 
-    T.ptx[f"ld{'.acquire.cta' if use_ld_acquire else 
''}.shared.b128"](response, handle)
+    T.ptx[f"ld{'.acquire.cta' if T.constexpr(use_ld_acquire) else 
''}.shared.b128"](
+        response, handle
+    )
     T.ptx.clusterlaunchcontrol.query_cancel.is_canceled.pred.b128(canceled, 
response)
     T.tensor_store(first_ctaid_x.source, T.uint32(0xFFFFFFFF), 
first_ctaid_x.indices)
     T.ptx.clusterlaunchcontrol.query_cancel.get_first_ctaid__x.b32.b128(
diff --git a/python/tvm/backend/cuda/lang/pipeline.py 
b/python/tvm/backend/cuda/lang/pipeline.py
index 5d21e30484..385fdf3007 100644
--- a/python/tvm/backend/cuda/lang/pipeline.py
+++ b/python/tvm/backend/cuda/lang/pipeline.py
@@ -73,7 +73,7 @@ class PipelineState:
 
     @T.inline
     def advance(self):
-        if self.depth > 1:
+        if T.constexpr(self.depth > 1):
             self.stage = self.stage + 1
             if self.stage == self.depth:
                 self.stage = 0
@@ -219,11 +219,13 @@ class MBarrier:
 
     @T.inline
     def _arrive(self, bar, pred=None, count=None):
-        if pred is None:
-            T.ptx.mbarrier.arrive.shared.b64(bar, T.uint32(1 if count is None 
else count))
+        if T.constexpr(pred is None):
+            T.ptx.mbarrier.arrive.shared.b64(
+                bar, T.uint32(1 if T.constexpr(count is None) else count)
+            )
         else:
             T.ptx.mbarrier.arrive.shared.b64(
-                bar, T.uint32(1 if count is None else count), pred=pred
+                bar, T.uint32(1 if T.constexpr(count is None) else count), 
pred=pred
             )
 
     def ptr_to(self, idx):
@@ -280,7 +282,7 @@ class TMABar(MBarrier):
 
     @T.inline
     def _arrive_tma_local(self, bar, tx_count=None):
-        if tx_count is None:
+        if T.constexpr(tx_count is None):
             T.ptx.mbarrier.arrive.shared.b64(bar, T.uint32(1))
         else:
             T.ptx.mbarrier.arrive.expect_tx.shared.b64(bar, T.uint32(tx_count))
@@ -307,7 +309,7 @@ class TCGen05Bar(MBarrier):
         # form cannot depend on it, so a runtime mask always multicasts.
         # ``pred`` rides the ptx keyword: the instruction is emitted
         # predicated (@p) rather than branched around.
-        if _tcgen05_commit_is_unicast(cta_mask):
+        if T.constexpr(_tcgen05_commit_is_unicast(cta_mask)):
             T.evaluate(
                 T.ptx[
                     f"tcgen05.commit.cta_group::{cta_group}"
diff --git a/python/tvm/backend/cuda/lang/tile_scheduler.py 
b/python/tvm/backend/cuda/lang/tile_scheduler.py
index f22902e20c..279b4524c2 100644
--- a/python/tvm/backend/cuda/lang/tile_scheduler.py
+++ b/python/tvm/backend/cuda/lang/tile_scheduler.py
@@ -821,7 +821,7 @@ class FlashAttentionLPTScheduler(BaseTileScheduler):
         :class:`FlashAttentionLinearScheduler`, while keeping the LPT + L2
         swizzle index mapping.
         """
-        if self._num_ctas is None:
+        if T.constexpr(self._num_ctas is None):
             self.linear_idx = self._total_tasks
         else:
             self.linear_idx = self.linear_idx + self._num_ctas
diff --git a/python/tvm/backend/cuda/tile_primitive/common.py 
b/python/tvm/backend/cuda/tile_primitive/common.py
index 00372b8a26..c24f7e570b 100644
--- a/python/tvm/backend/cuda/tile_primitive/common.py
+++ b/python/tvm/backend/cuda/tile_primitive/common.py
@@ -209,18 +209,18 @@ def copy_vec_load_impl(
             """Implement copy operation with vectorized loads/stores."""
             for s in T.serial(0, n_elements // (tx * vec_len)):
                 for tid_x in T.thread_binding(tx, "threadIdx.x"):
-                    if inst_type == CopyInstType.NORMAL:
+                    if T.constexpr(inst_type == CopyInstType.NORMAL):
                         for vec in T.vectorized(vec_len):
                             fused = T.meta_var((s * tx + tid_x) * vec_len + 
vec)
                             dst_indices = T.meta_var(get_indices(fused, 
dst_st, dst_extent))
                             src_indices = T.meta_var(get_indices(fused, 
src_st, src_extent))
                             dst[tuple(dst_indices)] = src[tuple(src_indices)]
-                    elif inst_type == CopyInstType.CP_ASYNC:
+                    elif T.constexpr(inst_type == CopyInstType.CP_ASYNC):
                         fused = T.meta_var((s * tx + tid_x) * vec_len)
                         dst_indices = T.meta_var(get_indices(fused, dst_st, 
dst_extent))
                         src_indices = T.meta_var(get_indices(fused, src_st, 
src_extent))
-                        T.evaluate(T.ptx[f"cp.async.{'cg' if cp_size == 16 
else 'ca'}.shared.global"](dst.ptr_to(dst_indices), src.ptr_to(src_indices), 
cp_size))  # noqa: E501
-            if dst.scope().startswith("shared") and inst_type == 
CopyInstType.NORMAL:
+                        T.evaluate(T.ptx[f"cp.async.{'cg' if 
T.constexpr(cp_size == 16) else 'ca'}.shared.global"](dst.ptr_to(dst_indices), 
src.ptr_to(src_indices), cp_size))  # noqa: E501
+            if T.constexpr(dst.scope().startswith("shared") and inst_type == 
CopyInstType.NORMAL):
                 T.tvm_storage_sync("shared")
         # fmt: on
     elif sctx.is_thread:
@@ -228,17 +228,17 @@ def copy_vec_load_impl(
         @T.function(check_well_formed=False)
         def impl():
             for s in T.serial(0, n_elements // (vec_len)):
-                if inst_type == CopyInstType.NORMAL:
+                if T.constexpr(inst_type == CopyInstType.NORMAL):
                     for vec in T.vectorized(vec_len):
                         fused = T.meta_var(s * vec_len + vec)
                         dst_indices = T.meta_var(get_indices(fused, dst_st, 
dst_extent))
                         src_indices = T.meta_var(get_indices(fused, src_st, 
src_extent))
                         dst[tuple(dst_indices)] = src[tuple(src_indices)]
-                elif inst_type == CopyInstType.CP_ASYNC:
+                elif T.constexpr(inst_type == CopyInstType.CP_ASYNC):
                     fused = T.meta_var(s * vec_len)
                     dst_indices = T.meta_var(get_indices(fused, dst_st, 
dst_extent))
                     src_indices = T.meta_var(get_indices(fused, src_st, 
src_extent))
-                    T.evaluate(T.ptx[f"cp.async.{'cg' if cp_size == 16 else 
'ca'}.shared.global"](dst.ptr_to(dst_indices), src.ptr_to(src_indices), 
cp_size))  # noqa: E501
+                    T.evaluate(T.ptx[f"cp.async.{'cg' if T.constexpr(cp_size 
== 16) else 'ca'}.shared.global"](dst.ptr_to(dst_indices), 
src.ptr_to(src_indices), cp_size))  # noqa: E501
         # fmt: on
     else:
         fail(f"unsupported exec_scope {sctx.scope_kind}")
diff --git a/python/tvm/backend/cuda/tile_primitive/copy/fallback.py 
b/python/tvm/backend/cuda/tile_primitive/copy/fallback.py
index c2c0ce9793..d60169e48d 100644
--- a/python/tvm/backend/cuda/tile_primitive/copy/fallback.py
+++ b/python/tvm/backend/cuda/tile_primitive/copy/fallback.py
@@ -22,6 +22,7 @@ import warnings
 import tvm
 from tvm.script import tirx as T
 from tvm.tirx import Function, Var
+from tvm.tirx.layout import ComposeLayout, TileLayout
 from tvm.tirx.operator.tile_primitive.dispatcher import (
     predicate,
     register_dispatch,
@@ -39,6 +40,22 @@ def _region_st_extent(buffer_region):
     return [r.min for r in region], [r.extent for r in region]
 
 
+def _is_scalar_copy(op_call: TilePrimitiveCall, sctx: DispatchContext):
+    ok, reason = _is_valid_copy(op_call, sctx)
+    if not ok:
+        return ok, reason
+    op_call = TilePrimitiveCall.downcast(op_call)
+    for region in (op_call.src, op_call.dst):
+        layout = region.source.layout
+        if isinstance(layout, ComposeLayout):
+            layout = layout.tile_layout
+        if isinstance(layout, TileLayout) and any(
+            it.axis.is_thread() for it in (*layout.shard, *layout.replica)
+        ):
+            return False, "single-thread fallback cannot access a tensor with 
thread axes"
+    return True, None
+
+
 def _emit_fallback(op_call: TilePrimitiveCall, sctx: DispatchContext) -> 
Function:
     op_call = TilePrimitiveCall.downcast(op_call)
     src: Var = op_call.src.source
@@ -114,7 +131,7 @@ def _emit_fallback(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Functio
     "cuda",
     variant="fallback",
     priority=0,
-    when=[predicate("validate_copy_op", _is_valid_copy)],
+    when=[predicate("scalar_copy_applicable", _is_scalar_copy)],
 )
 def copy_schedule_fallback(op_call: TilePrimitiveCall, sctx: DispatchContext) 
-> Function:
     return _emit_fallback(op_call, sctx)
diff --git a/python/tvm/backend/cuda/tile_primitive/copy/ld_stmatrix.py 
b/python/tvm/backend/cuda/tile_primitive/copy/ld_stmatrix.py
index c4c7eac1c5..73c6022ae9 100644
--- a/python/tvm/backend/cuda/tile_primitive/copy/ld_stmatrix.py
+++ b/python/tvm/backend/cuda/tile_primitive/copy/ld_stmatrix.py
@@ -376,7 +376,7 @@ def _emit(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Function:
                 r_words[r.apply(0, 0, 0, mm, i, 0, 
shape=apply_shape)[r_mem_axis] // 2]
                 for i in range(num)
             ]
-            if direction == "ld":
+            if T.constexpr(direction == "ld"):
                 T.ptx[ld_chain](*words, smem_ptr)
             else:
                 # stmatrix reverses ldmatrix's operand order: address first.
diff --git a/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_gmem_smem.py 
b/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_gmem_smem.py
index 246e7b5fb6..dee51b8315 100644
--- a/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_gmem_smem.py
+++ b/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_gmem_smem.py
@@ -176,7 +176,7 @@ def _emit_gmem_smem(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Functi
             s_off = s_apply_layout.apply(f, tid, v0, shape=apply_shape)["m"]
             s_ptr = _ptr_off(s_buf.ptr_to(s_zero), s_off)
             g_ptr = _ptr_off(g_buf.ptr_to(g_zero), g_lin)
-            if g_is_src:
+            if T.constexpr(g_is_src):
                 T.ptx[ld_g](*[tmp[i] for i in range(lanes)], g_ptr)
                 T.ptx[st_s](s_ptr, *[tmp[i] for i in range(lanes)])
             else:
diff --git a/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_reg.py 
b/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_reg.py
index 17412937b9..f57bc9d92b 100644
--- a/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_reg.py
+++ b/python/tvm/backend/cuda/tile_primitive/copy/vec_auto_reg.py
@@ -599,7 +599,7 @@ def _emit_reg(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Function:
 
     @T.function(check_well_formed=False)
     def impl():
-        if not has_swizzle:
+        if T.constexpr(not has_swizzle):
             s_base = _apply_s_layout(
                 s_apply_layout, thread_coords, 0, s_apply_shape, placeholders, 
sctx
             )
@@ -612,7 +612,7 @@ def _emit_reg(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Function:
         # replicates per-iter scratch arrays and pressures registers.
         for f in range(total_outer):
             ds, dr = _outer_const_offsets(outer, f)
-            if has_swizzle:
+            if T.constexpr(has_swizzle):
                 s_off = _apply_s_layout(
                     s_apply_layout, thread_coords, f, s_apply_shape, 
placeholders, sctx
                 )
@@ -623,7 +623,7 @@ def _emit_reg(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Function:
             # element pointer: the container index is the element offset scaled
             # between element and word granularity.
             r_w = (r_off_base + dr) * words_per_elem // elems_per_word
-            if r_is_src:
+            if T.constexpr(r_is_src):
                 T.ptx[st_chain](s_ptr, *[r_words[r_w + i] for i in 
range(lanes)])
             else:
                 T.ptx[ld_chain](*[r_words[r_w + i] for i in range(lanes)], 
s_ptr)
diff --git a/python/tvm/backend/cuda/tile_primitive/copy/vec_forced.py 
b/python/tvm/backend/cuda/tile_primitive/copy/vec_forced.py
index f3b72e9648..57afd2de2e 100644
--- a/python/tvm/backend/cuda/tile_primitive/copy/vec_forced.py
+++ b/python/tvm/backend/cuda/tile_primitive/copy/vec_forced.py
@@ -191,9 +191,9 @@ def _emit_forced_vec_copy(op_call: TilePrimitiveCall, 
_sctx: DispatchContext, nu
     # fmt: off
     @T.function(check_well_formed=False)
     def impl():
-        if src_is_local:
+        if T.constexpr(src_is_local):
             T.ptx[st_chain](dst_ptr, *_words(src, op_call.src))
-        elif dst_is_local:
+        elif T.constexpr(dst_is_local):
             T.ptx[ld_chain](*_words(dst, op_call.dst), src_ptr)
         else:
             tmp = T.alloc_local((lanes,), reg_dtype)
diff --git a/python/tvm/backend/cuda/tile_primitive/copy_async/dsmem.py 
b/python/tvm/backend/cuda/tile_primitive/copy_async/dsmem.py
index 041fbeb37e..a37f4845a1 100644
--- a/python/tvm/backend/cuda/tile_primitive/copy_async/dsmem.py
+++ b/python/tvm/backend/cuda/tile_primitive/copy_async/dsmem.py
@@ -134,6 +134,8 @@ def copy_dsmem_impl(op_call: TilePrimitiveCall, sctx: 
DispatchContext) -> Functi
 
     # Helper to compute element offsets from loop variables (called via 
T.meta_var)
     def compute_offsets(loop_vars):
+        if isinstance(loop_vars, Var):
+            loop_vars = (loop_vars,)
         src_off = 0
         dst_off = 0
         for j, v in enumerate(loop_vars):
diff --git a/python/tvm/backend/cuda/tile_primitive/elementwise/_common.py 
b/python/tvm/backend/cuda/tile_primitive/elementwise/_common.py
index 90aea6abc5..21619d03fc 100644
--- a/python/tvm/backend/cuda/tile_primitive/elementwise/_common.py
+++ b/python/tvm/backend/cuda/tile_primitive/elementwise/_common.py
@@ -379,11 +379,11 @@ def emit_scope_sync(scope_kind: str):
 
     @T.inline
     def sync():
-        if scope_kind == "cta":
+        if T.constexpr(scope_kind == "cta"):
             T.cuda.cta_sync()
-        elif scope_kind == "warpgroup":
+        elif T.constexpr(scope_kind == "warpgroup"):
             T.cuda.warpgroup_sync(8)
-        elif scope_kind == "warp":
+        elif T.constexpr(scope_kind == "warp"):
             T.cuda.warp_sync()
 
     return sync
diff --git a/python/tvm/backend/cuda/tile_primitive/elementwise/reg.py 
b/python/tvm/backend/cuda/tile_primitive/elementwise/reg.py
index cc72a9d964..5854febc64 100644
--- a/python/tvm/backend/cuda/tile_primitive/elementwise/reg.py
+++ b/python/tvm/backend/cuda/tile_primitive/elementwise/reg.py
@@ -389,7 +389,7 @@ def _emit_induced_packed(
             src_args = T.meta_var(
                 [
                     src.scalar
-                    if src.is_scalar
+                    if T.constexpr(src.is_scalar)
                     else (
                         views[src.buf_region],
                         [[f * vec_len + k] for k in range(vec_len)],
@@ -423,7 +423,10 @@ def _emit_induced_scalar(
             # Logical 1D coord = f (vec_len = 1 in scalar path); each
             # buffer's layout maps to physical at access time.
             src_vals = T.meta_var(
-                [src.scalar if src.is_scalar else views[src.buf_region][f] for 
src in srcs]
+                [
+                    src.scalar if T.constexpr(src.is_scalar) else 
views[src.buf_region][f]
+                    for src in srcs
+                ]
             )
             views[dst_br][f] = T.cast(compute(src_vals, extras, dst_dtype), 
dst_dtype)
 
diff --git a/python/tvm/backend/cuda/tile_primitive/elementwise/smem.py 
b/python/tvm/backend/cuda/tile_primitive/elementwise/smem.py
index 9b5178825c..82b2036feb 100644
--- a/python/tvm/backend/cuda/tile_primitive/elementwise/smem.py
+++ b/python/tvm/backend/cuda/tile_primitive/elementwise/smem.py
@@ -212,7 +212,7 @@ def _emit_packed(plan, vec_impl, vec_chunk, total, 
thread_cnt, sctx) -> Function
                 src_args = T.meta_var(
                     [
                         srcs[i].scalar
-                        if srcs[i].is_scalar
+                        if T.constexpr(srcs[i].is_scalar)
                         else (
                             srcs[i].buf_region.source,
                             _src_lane_indices(
diff --git a/python/tvm/backend/cuda/tile_primitive/gemm/mma_m16n8k_.py 
b/python/tvm/backend/cuda/tile_primitive/gemm/mma_m16n8k_.py
index 8e28a2e712..ed2c50d0cd 100644
--- a/python/tvm/backend/cuda/tile_primitive/gemm/mma_m16n8k_.py
+++ b/python/tvm/backend/cuda/tile_primitive/gemm/mma_m16n8k_.py
@@ -570,7 +570,7 @@ def gemm_cuda_mma_dispatch(op: TilePrimitiveCall, sctx: 
DispatchContext) -> Func
                 # Initialize D[m, n]: copy C (beta==1) or clear to 0 (beta==0).
                 for rM in T.unroll(n_rM):
                     for rN in T.unroll(n_rN):
-                        if use_c:
+                        if T.constexpr(use_c):
                             d_local[m, n, rM, rN] = c_local[m, n, rM, rN]
                         else:
                             d_local[m, n, rM, rN] = T.float32(0)
diff --git a/python/tvm/backend/cuda/tile_primitive/gemm_async/tcgen05.py 
b/python/tvm/backend/cuda/tile_primitive/gemm_async/tcgen05.py
index f3e43707bc..de5b7983b1 100644
--- a/python/tvm/backend/cuda/tile_primitive/gemm_async/tcgen05.py
+++ b/python/tvm/backend/cuda/tile_primitive/gemm_async/tcgen05.py
@@ -1290,6 +1290,13 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
     # and uses rows 64-127 for the other half.
     N_mma_phys_cols = N_mma // 2 if is_2x2 or packed_n2 else N_mma
 
+    def _d_operand(mi, ni):
+        # Simplify coordinates before TVMScript binds them to IR variables, so
+        # constant lane offsets remain visible to the address fast path.
+        row = tmem_lane_offset + (0 if M_tiles == 1 else mi * M_mma)
+        col = tmem_offset_32b + ni * (N_mma_phys_cols // C_elem_per_32b)
+        return _get_tmem_addr_fast(tmem_addr, row, col)
+
     # The ptx instruction spelling, resolved once from the trace-time dtypes.
     if is_block_scaled:
         _bs_kind = _get_tcgen05_mma_kind(C_type, A_type, B_type, SFA_type, 
SFB_type)
@@ -1326,7 +1333,6 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
             for mi in T.unroll(M_tiles):
               for ni in T.unroll(N_tiles):
                 for ki in T.unroll(K_iters):
-                    # meta_var inlines operands into mma.block_scale (avoids 
LMEM temps).
                     a_val = T.meta_var(_a_operand(mi, ki, descA_in))
                     descB_val = T.meta_var(_b_desc_val(descB_in, ni, ki))
                     should_accum = T.meta_var(tvm.tirx.any(ki != 0, 
accum_expr))
@@ -1353,32 +1359,18 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
                             tvm.tirx.floordiv(sfb_tcol, SFB_elem_per_col),
                         )
                     )
-                    if needs_sf_id:
+                    if T.constexpr(needs_sf_id):
                         sf_id = 
T.meta_var(analyzer.simplify(tvm.tirx.floormod(sfa_tcol, SFA_elem_per_col)))  # 
noqa: E501
                         T.cuda.runtime_instr_desc(T.address_of(descI_in), 
sf_id)
-                    tmem_col = T.meta_var(
-                        tmem_offset_32b + ni * (N_mma_phys_cols // 
C_elem_per_32b)
-                    )
-                    tmem_row = T.meta_var(
-                        tmem_lane_offset + (0 if M_tiles == 1 else mi * M_mma)
-                    )
                     if elect_pred:
                         T.evaluate(T.ptx[mma_chain](
-                            T.cast(_get_tmem_addr_fast(tmem_addr, tmem_row, 
tmem_col), "uint32"),
-                            T.cast(a_val, "uint32") if a_is_tmem else a_val,
+                            T.cast(_d_operand(mi, ni), "uint32"),
+                            T.cast(a_val, "uint32") if T.constexpr(a_is_tmem) 
else a_val,
                             descB_val, descI_in,
                             T.cast(sfa_addr, "uint32"), T.cast(sfb_addr, 
"uint32"),
                             should_accum,
                         ))
     else:
-        # Wrap each per-MMA operand in ``T.meta_var`` so the parser inlines
-        # the value directly into the tcgen05.mma call instead of
-        # materializing it into a fresh ``alignas(64) T x[1]; x[0] = expr``
-        # local. Without this wrap each unrolled MMA emits 4 throw-away
-        # 1-element local arrays (``a_val_ptr``, ``descB_val_ptr``,
-        # ``should_accum_ptr``, ``tmem_col_ptr``) which ptxas cannot fold
-        # back into the operand and the resulting LMEM round-trips show up
-        # on the fa4 hot path.
         if encode_per_mma:
             @T.inline
             def main_impl(descA_in, descB_in, descI_in):
@@ -1386,22 +1378,16 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
                   for ni in T.unroll(N_tiles):
                     for ki in T.unroll(K_iters):
                         should_accum = T.meta_var(tvm.tirx.any(ki != 0, 
accum_expr))
-                        tmem_col = T.meta_var(
-                            tmem_offset_32b + ni * (N_mma_phys_cols // 
C_elem_per_32b)
-                        )
-                        tmem_row = T.meta_var(
-                            tmem_lane_offset + (0 if M_tiles == 1 else mi * 
M_mma)
-                        )
                         if elect_pred:
                             descB_mma = T.meta_var(
                                 _encoded_desc_val(
                                     B_buffer, _b_offset(ni, ki), B_ldo, B_sdo, 
B_swizzle_mode.value
                                 )
                             )
-                            if a_is_tmem:
+                            if T.constexpr(a_is_tmem):
                                 a_val = T.meta_var(_a_operand(mi, ki, 
descA_in))
                                 _emit_mma(
-                                    _get_tmem_addr_fast(tmem_addr, tmem_row, 
tmem_col),
+                                    _d_operand(mi, ni),
                                     a_val, descB_mma, descI_in, should_accum,
                                 )
                             else:
@@ -1415,7 +1401,7 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
                                     )
                                 )
                                 _emit_mma(
-                                    _get_tmem_addr_fast(tmem_addr, tmem_row, 
tmem_col),
+                                    _d_operand(mi, ni),
                                     descA_mma, descB_mma, descI_in, 
should_accum,
                                 )
         else:
@@ -1427,15 +1413,9 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
                         a_val = T.meta_var(_a_operand(mi, ki, descA_in))
                         descB_val = T.meta_var(_b_desc_val(descB_in, ni, ki))
                         should_accum = T.meta_var(tvm.tirx.any(ki != 0, 
accum_expr))
-                        tmem_col = T.meta_var(
-                            tmem_offset_32b + ni * (N_mma_phys_cols // 
C_elem_per_32b)
-                        )
-                        tmem_row = T.meta_var(
-                            tmem_lane_offset + (0 if M_tiles == 1 else mi * 
M_mma)
-                        )
                         if elect_pred:
                             _emit_mma(
-                                _get_tmem_addr_fast(tmem_addr, tmem_row, 
tmem_col),
+                                _d_operand(mi, ni),
                                 a_val, descB_val, descI_in, should_accum,
                             )
 
@@ -1443,7 +1423,7 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
 
     @T.inline
     def call_main(descI_arg):
-        if local_hoist:
+        if T.constexpr(local_hoist):
             descB_local = T.alloc_local((1,), "uint64")
             T.cuda.tcgen05.encode_matrix_descriptor(
                 T.address_of(descB_local[0]),
@@ -1454,7 +1434,7 @@ def gemm_async_tcgen05_impl(op_call: TilePrimitiveCall, 
sctx: DispatchContext) -
             )
             # local_hoist runs at the call site under elected-thread control, 
so the
             # descriptor is consumed by the same thread and needs no warp 
shuffle.
-            if A_use_add:
+            if T.constexpr(A_use_add):
                 descA_local = T.alloc_local((1,), "uint64")
                 T.cuda.tcgen05.encode_matrix_descriptor(
                     T.address_of(descA_local[0]),
diff --git 
a/python/tvm/backend/cuda/tile_primitive/permute_layout/warp_xor_swizzle.py 
b/python/tvm/backend/cuda/tile_primitive/permute_layout/warp_xor_swizzle.py
index 36787460da..e141c1e9fe 100644
--- a/python/tvm/backend/cuda/tile_primitive/permute_layout/warp_xor_swizzle.py
+++ b/python/tvm/backend/cuda/tile_primitive/permute_layout/warp_xor_swizzle.py
@@ -328,7 +328,7 @@ def _impl(op_call, sctx):
 
     # fmt: off
     if direct:
-        @T.function
+        @T.function(check_well_formed=False)
         def impl():
             warp_size = T.meta_var(32)
             lane_id = T.meta_var(tid_x % warp_size)
@@ -354,7 +354,7 @@ def _impl(op_call, sctx):
                 T.ptx[st_chain](ptr, regs[r])
             T.cuda.warp_sync()
     else:
-        @T.function
+        @T.function(check_well_formed=False)
         def impl():
             warp_size = T.meta_var(32)
             lane_id = T.meta_var(tid_x % warp_size)
diff --git a/python/tvm/backend/cuda/tile_primitive/reduction/local.py 
b/python/tvm/backend/cuda/tile_primitive/reduction/local.py
index a328757aac..feda434141 100644
--- a/python/tvm/backend/cuda/tile_primitive/reduction/local.py
+++ b/python/tvm/backend/cuda/tile_primitive/reduction/local.py
@@ -141,7 +141,7 @@ def _gen_warp_shuffle_reduce(src, dst, reduce_width, 
local_elems, accum, op_type
         src_local = src.local(local_elems, layout=src.layout.storage())
         dst_local = dst.local(local_elems, layout=dst.layout.storage())
         for k in T.serial(local_elems):
-            if not is_same_buffer:
+            if T.constexpr(not is_same_buffer):
                 dst_local[k] = src_local[k]
             dst_local[k] = T.cuda.warp_reduce(dst_local[k], op_str, 
reduce_width)
     # fmt: on
@@ -273,7 +273,7 @@ def _emit_reduction_local_thread_wise(
     def impl():
         for spa in T.serial(spatial_len):
             dst_idx = T.meta_var(get_indices(spa, dst_st, dst_extent))
-            if not accum:
+            if T.constexpr(not accum):
                 dst[tuple(dst_idx)] = init_value
             for red in T.serial(reduction_len):
                 src_idx = T.meta_var(get_src_indices(spa, red))
@@ -367,12 +367,12 @@ def _emit_reduction_local_view(
             for spa in T.serial(dst_local_total):
                 dst_idx = T.meta_var(get_indices(spa, dst_local_st, 
dst_local_ext))
                 old_val[0] = dst_local[tuple(dst_idx)]
-                if not in_place:
+                if T.constexpr(not in_place):
                     dst_local[tuple(dst_idx)] = init_value
                     for red in T.serial(reduction_local_total):
                         src_idx = T.meta_var(_get_src_local_index(spa, red))
                         dst_local[tuple(dst_idx)] = 
op_func(dst_local[tuple(dst_idx)], src_local[tuple(src_idx)])  # noqa: E501
-                if shuffle:
+                if T.constexpr(shuffle):
                     mask = T.tvm_warp_activemask()
                     shuffle_data(mask, dst_local, dst_idx)
                 dst_local[tuple(dst_idx)] = op_func(dst_local[tuple(dst_idx)], 
old_val[0])
@@ -386,13 +386,13 @@ def _emit_reduction_local_view(
 
             for spa in T.serial(dst_local_total):
                 dst_idx = T.meta_var(get_indices(spa, dst_local_st, 
dst_local_ext))
-                if not in_place:
-                    if not accum:
+                if T.constexpr(not in_place):
+                    if T.constexpr(not accum):
                         dst_local[tuple(dst_idx)] = init_value
                     for red in T.serial(reduction_local_total):
                         src_idx = T.meta_var(_get_src_local_index(spa, red))
                         dst_local[tuple(dst_idx)] = 
op_func(dst_local[tuple(dst_idx)], src_local[tuple(src_idx)])  # noqa: E501
-                if shuffle:
+                if T.constexpr(shuffle):
                     mask = T.tvm_warp_activemask()
                     shuffle_data(mask, dst_local, dst_idx)
     # fmt: on
diff --git a/python/tvm/backend/cuda/tile_primitive/reduction/shared.py 
b/python/tvm/backend/cuda/tile_primitive/reduction/shared.py
index 72c3a46da3..cfcf135a4d 100644
--- a/python/tvm/backend/cuda/tile_primitive/reduction/shared.py
+++ b/python/tvm/backend/cuda/tile_primitive/reduction/shared.py
@@ -179,17 +179,17 @@ def _emit_reduction_shared_cta(
 
     @T.inline
     def sync():
-        if exec_scope_name == "cta":
+        if T.constexpr(exec_scope_name == "cta"):
             T.cuda.cta_sync()
-        elif exec_scope_name == "warpgroup":
+        elif T.constexpr(exec_scope_name == "warpgroup"):
             T.cuda.warpgroup_sync(8)  # TODO: fix this hardcoded value
-        elif exec_scope_name == "warp":
+        elif T.constexpr(exec_scope_name == "warp"):
             T.cuda.warp_sync()
-        elif exec_scope_name == "thread":
+        elif T.constexpr(exec_scope_name == "thread"):
             pass
 
     # fmt: off
-    @T.function
+    @T.function(check_well_formed=False)
     def impl():
         tid_in_scope = get_tid_in_scope()
         thread_data = T.alloc_tensor([1], dtype=dtype, scope="local")
@@ -237,11 +237,11 @@ def _emit_reduction_shared_thread(
     assert op_func is not None
     init_value = reduce_default_value_table(dtype).get(reduce_op)
 
-    @T.function
+    @T.function(check_well_formed=False)
     def impl():
         for spa_fused in T.serial(spatial_len):
             dst_indices = T.meta_var(get_indices(spa_fused, dst_st, 
dst_extent))
-            if not accum:
+            if T.constexpr(not accum):
                 dst[tuple(dst_indices)] = init_value
             for red_fused in T.serial(reduction_len):
                 src_indices = T.meta_var(
diff --git a/src/tirx/op/builtin.cc b/src/tirx/op/builtin.cc
index ee755d37ef..38cdd0d866 100644
--- a/src/tirx/op/builtin.cc
+++ b/src/tirx/op/builtin.cc
@@ -37,14 +37,15 @@ void CallFFIKernelAttr::RegisterReflection() {
 }
 
 void TensorMapEncodeTiledAttr::RegisterReflection() {
+  namespace refl = ffi::reflection;
   ffi::reflection::ObjectDef<TensorMapEncodeTiledAttr>()
       .def_ro("descriptor_dtype", &TensorMapEncodeTiledAttr::descriptor_dtype)
       .def_ro("rank", &TensorMapEncodeTiledAttr::rank)
-      .def_ro("interleave", &TensorMapEncodeTiledAttr::interleave)
-      .def_ro("swizzle", &TensorMapEncodeTiledAttr::swizzle)
-      .def_ro("l2_promotion", &TensorMapEncodeTiledAttr::l2_promotion)
-      .def_ro("oob_fill", &TensorMapEncodeTiledAttr::oob_fill)
-      .def_ro("force_cu_dtype", &TensorMapEncodeTiledAttr::force_cu_dtype);
+      .def_ro("interleave", &TensorMapEncodeTiledAttr::interleave, 
refl::DefaultValue(0))
+      .def_ro("swizzle", &TensorMapEncodeTiledAttr::swizzle, 
refl::DefaultValue(0))
+      .def_ro("l2_promotion", &TensorMapEncodeTiledAttr::l2_promotion, 
refl::DefaultValue(0))
+      .def_ro("oob_fill", &TensorMapEncodeTiledAttr::oob_fill, 
refl::DefaultValue(0))
+      .def_ro("force_cu_dtype", &TensorMapEncodeTiledAttr::force_cu_dtype, 
refl::DefaultValue(-1));
 }
 
 ffi::Array<Var> LaunchThreadBodyParams(const CallNode* call) {
@@ -567,7 +568,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
   OpDef("tirx.tensormap_encode_tiled")
       .set_attr<TFixedReturnType>("TFixedReturnType", PrimType::Int(32))
       .signature(sig::arg("descriptor", "The descriptor."), sig::arg("data", 
"The input data."),
-                 sig::var_args("args"))
+                 sig::var_args("args"), 
sig::call_attrs<TensorMapEncodeTiledAttr>())
       .set_attr<TScriptPrinterName>("TScriptPrinterName",
                                     ffi::String("tirx.tensormap_encode_tiled"))
       .set_attr<TIRxOpCategory>("TIRxOpCategory", ffi::String("builtin"))
diff --git a/src/tirx/transform/lower_tirx_cleanup.cc 
b/src/tirx/transform/lower_tirx_cleanup.cc
index afab85154c..b71d58b719 100644
--- a/src/tirx/transform/lower_tirx_cleanup.cc
+++ b/src/tirx/transform/lower_tirx_cleanup.cc
@@ -235,6 +235,14 @@ class LayoutApplier : public IRMutatorWithAnalyzer {
                  .ValueOrUnchanged(type->strides[i])
                  .as_or_throw<PrimExpr>());
     }
+    // TMEM addresses may load from an allocation whose variable was rebuilt
+    // above. Keep the type metadata in sync with the declaration's pointer.
+    for (size_t i = 0; i < type->allocated_addr.size(); ++i) {
+      type->allocated_addr.Set(
+          i, StmtExprMutator::Mutate(ffi::AnyView(type->allocated_addr[i]), 
InplaceMode::kDisallow)
+                 .ValueOrUnchanged(type->allocated_addr[i])
+                 .as_or_throw<PrimExpr>());
+    }
     type->layout = std::nullopt;
     type->elem_offset =
         StmtExprMutator::Mutate(ffi::AnyView(buf->elem_offset), 
InplaceMode::kDisallow)
diff --git a/tests/python/tirx/codegen/test_codegen_ampere.py 
b/tests/python/tirx/codegen/test_codegen_ampere.py
index 754a026aa2..0fea3668a9 100644
--- a/tests/python/tirx/codegen/test_codegen_ampere.py
+++ b/tests/python/tirx/codegen/test_codegen_ampere.py
@@ -105,13 +105,13 @@ def test_ptx_mma_m16n8k16(a_type, no_c_ptr):
 
         @T.inline
         def G2L(buf_local, buf_global, block_8x8, mode="row"):
-            if mode == "row":
+            if T.constexpr(mode == "row"):
                 for i in range(block_8x8):
                     row = T.meta_var(i % 2 * 8 + tx // 4)
                     col = T.meta_var(i // 2 * 8 + (tx % 4) * 2)
                     for j in range(2):
                         buf_local[i * 2 + j] = buf_global[row, col + j]
-            elif mode == "col":
+            elif T.constexpr(mode == "col"):
                 for i in range(block_8x8):
                     row = T.meta_var(i % 2 * 8 + (tx % 4) * 2)
                     col = T.meta_var(i // 2 * 8 + tx // 4)
@@ -129,7 +129,7 @@ def test_ptx_mma_m16n8k16(a_type, no_c_ptr):
         B_words = B_local.view("uint32")
         # ptx takes the accumulator as an operand; the legacy "omit c" form
         # fed literal zeros, which is now spelled at the call site.
-        if no_c_ptr:
+        if T.constexpr(no_c_ptr):
             for i in range(4):
                 C_local[i] = T.float32(0)
         T.ptx[f"mma.sync.aligned.m16n8k16.row.col.f32.{_elem}.{_elem}.f32"](
@@ -182,13 +182,13 @@ def test_ptx_mma_m16n8k8(a_type, no_c_ptr):
 
         @T.inline
         def G2L(buf_local, buf_global, block_8x8, mode="row"):
-            if mode == "row":
+            if T.constexpr(mode == "row"):
                 for i in range(block_8x8):
                     row = T.meta_var(i % 2 * 8 + tx // 4)
                     col = T.meta_var(i // 2 * 8 + (tx % 4) * 2)
                     for j in range(2):
                         buf_local[i * 2 + j] = buf_global[row, col + j]
-            elif mode == "col":
+            elif T.constexpr(mode == "col"):
                 for i in range(block_8x8):
                     row = T.meta_var(i % 2 * 8 + (tx % 4) * 2)
                     col = T.meta_var(i // 2 * 8 + tx // 4)
@@ -205,7 +205,7 @@ def test_ptx_mma_m16n8k8(a_type, no_c_ptr):
         B_words = B_local.view("uint32")
         # ptx takes the accumulator as an operand; the legacy "omit c" form
         # fed literal zeros, which is now spelled at the call site.
-        if no_c_ptr:
+        if T.constexpr(no_c_ptr):
             for i in range(4):
                 C_local[i] = T.float32(0)
         T.ptx[f"mma.sync.aligned.m16n8k8.row.col.f32.{_elem}.{_elem}.f32"](
diff --git a/tests/python/tirx/codegen/test_codegen_cuda.py 
b/tests/python/tirx/codegen/test_codegen_cuda.py
index 21aa7428a6..9a782adc1c 100644
--- a/tests/python/tirx/codegen/test_codegen_cuda.py
+++ b/tests/python/tirx/codegen/test_codegen_cuda.py
@@ -1108,9 +1108,9 @@ def test_ptx_cp_async(cp_size, cache_hint, prefetch_size, 
predicate, fill_mode):
         for i in T.vectorized(N):
             A_shared[i] = 5.0
         T.ptx.fence.proxy.async_.shared__cta()
-        if fill_mode == "zero":
+        if T.constexpr(fill_mode == "zero"):
             T.ptx[chain](A_shared.ptr_to([0]), A.ptr_to([0]), cp_size, 
src_size, *cache_args)
-        elif has_pred:
+        elif T.constexpr(has_pred):
             T.ptx[chain](A_shared.ptr_to([0]), A.ptr_to([0]), cp_size, 
*cache_args, pred=predicate)
         else:
             T.ptx[chain](A_shared.ptr_to([0]), A.ptr_to([0]), cp_size, 
*cache_args)
@@ -1161,19 +1161,28 @@ def test_ptx_ldmatrix(trans, num):
         # contiguous fp16[8] buffer, so the registers land through a uint32
         # view, two fp16 elements per word.
         A_words = A_local.view("uint32")
-        if num == 1:
-            T.ptx[f"ldmatrix.sync.aligned.m8n8.x1{'.trans' if trans else 
''}.shared.b16"](
+        if T.constexpr(num == 1):
+            T.ptx[
+                "ldmatrix.sync.aligned.m8n8.x1"
+                f"{'.trans' if T.constexpr(trans) else ''}.shared.b16"
+            ](
                 A_words[0],
                 A_shared.ptr_to([tx % 16, tx // 16 * 8]),
             )
-        elif num == 2:
-            T.ptx[f"ldmatrix.sync.aligned.m8n8.x2{'.trans' if trans else 
''}.shared.b16"](
+        elif T.constexpr(num == 2):
+            T.ptx[
+                "ldmatrix.sync.aligned.m8n8.x2"
+                f"{'.trans' if T.constexpr(trans) else ''}.shared.b16"
+            ](
                 A_words[0],
                 A_words[1],
                 A_shared.ptr_to([tx % 16, tx // 16 * 8]),
             )
         else:
-            T.ptx[f"ldmatrix.sync.aligned.m8n8.x4{'.trans' if trans else 
''}.shared.b16"](
+            T.ptx[
+                "ldmatrix.sync.aligned.m8n8.x4"
+                f"{'.trans' if T.constexpr(trans) else ''}.shared.b16"
+            ](
                 A_words[0],
                 A_words[1],
                 A_words[2],
diff --git a/tests/python/tirx/codegen/test_codegen_hopper.py 
b/tests/python/tirx/codegen/test_codegen_hopper.py
index f8e4dc467f..ad9d48bec6 100644
--- a/tests/python/tirx/codegen/test_codegen_hopper.py
+++ b/tests/python/tirx/codegen/test_codegen_hopper.py
@@ -69,7 +69,7 @@ def test_ptx_setmaxnreg(inc):
         T.device_entry()
         cta_id = T.cta_id([1])
         tid = T.thread_id([128])
-        T.ptx[f"setmaxnreg.{'inc' if inc else 'dec'}.sync.aligned.u32"](32)
+        T.ptx[f"setmaxnreg.{'inc' if T.constexpr(inc) else 
'dec'}.sync.aligned.u32"](32)
         # fmt: on
 
     src, mod = _get_source(func)
@@ -97,7 +97,7 @@ def test_stmatrix_sync_aligned(trans):
         # stmatrix stores 4 b32 registers; reg is fp16, so they ride a uint32
         # view, two elements per word.
         reg_words = reg.view("uint32")
-        T.ptx[f"stmatrix.sync.aligned.m8n8.x4{'.trans' if trans else 
''}.shared.b16"](
+        T.ptx[f"stmatrix.sync.aligned.m8n8.x4{'.trans' if T.constexpr(trans) 
else ''}.shared.b16"](
             A_smem.ptr_to([tx % 16, tx // 16 * 8]),
             reg_words[0], reg_words[1], reg_words[2], reg_words[3],
         )
@@ -166,7 +166,10 @@ def test_ptx_stmatrix(trans, num):
         for i in range(8):
             A_local[i] = (i // 2) * 64 + tx * 2 + i % 2
         A_words = A_local.view("uint32")
-        T.ptx[f"stmatrix.sync.aligned.m8n8.x{num}{'.trans' if trans else 
''}.shared.b16"](
+        T.ptx[
+            f"stmatrix.sync.aligned.m8n8.x{num}"
+            f"{'.trans' if T.constexpr(trans) else ''}.shared.b16"
+        ](
             A_shared.ptr_to([tx % 16, tx // 16 * 8]),
             *[A_words[i] for i in range(num)],
         )
@@ -239,7 +242,10 @@ def test_ptx_stmatrix_noncontiguous(trans, num):
             A_local[i * STRIDE + 0] = T.float16(i * 64 + tx * 2 + 0)
             A_local[i * STRIDE + 1] = T.float16(i * 64 + tx * 2 + 1)
         A_words = A_local.view("uint32")
-        T.ptx[f"stmatrix.sync.aligned.m8n8.x{num}{'.trans' if trans else 
''}.shared.b16"](
+        T.ptx[
+            f"stmatrix.sync.aligned.m8n8.x{num}"
+            f"{'.trans' if T.constexpr(trans) else ''}.shared.b16"
+        ](
             A_shared.ptr_to([tx % 16, tx // 16 * 8]),
             *[A_words[i * STRIDE // 2] for i in range(num)],
         )
diff --git a/tests/python/tirx/codegen/test_codegen_nvshmem.py 
b/tests/python/tirx/codegen/test_codegen_nvshmem.py
index 16d82cb9fc..1516ef199e 100644
--- a/tests/python/tirx/codegen/test_codegen_nvshmem.py
+++ b/tests/python/tirx/codegen/test_codegen_nvshmem.py
@@ -143,7 +143,7 @@ def test_codegen_nvshmem():
                 my_pe = T.nvshmem.my_pe()
                 n_pes = T.nvshmem.n_pes()
                 dst_pe = (my_pe + 1) % n_pes
-                if sig_op == "add":
+                if T.constexpr(sig_op == "add"):
                     res[0] = 1
                 T.nvshmem.barrier_all()
                 T.nvshmem.signal_op(sig_addr=res.ptr_to([0]), signal=1, 
sig_op=sig_op, pe=dst_pe)
diff --git a/tests/python/tirx/codegen/test_ptx_ld_st_ops.py 
b/tests/python/tirx/codegen/test_ptx_ld_st_ops.py
index 5f12ba5059..036c171380 100644
--- a/tests/python/tirx/codegen/test_ptx_ld_st_ops.py
+++ b/tests/python/tirx/codegen/test_ptx_ld_st_ops.py
@@ -83,13 +83,13 @@ def _shared_scratch_copy_kernel(num_bytes: int):
         src_buf = T.alloc_tensor((nelems,), smem_dtype, scope="shared")
         dst_buf = T.alloc_tensor((nelems,), smem_dtype, scope="shared")
         tmp = T.alloc_local((lanes,), reg_dtype)
-        if fill_offset is not None:
+        if T.constexpr(fill_offset is not None):
             if lane < nelems:
                 src_buf[lane] = T.uint32(lane + fill_offset)
-        elif fill_fp16 is not None:
+        elif T.constexpr(fill_fp16 is not None):
             if lane == 0:
                 src_buf[0] = T.float16(fill_fp16)
-        elif fill_u8 is not None:
+        elif T.constexpr(fill_u8 is not None):
             if lane == 0:
                 src_buf[0] = T.uint8(fill_u8)
         elif lane == 0:
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/copy/test_gmem_smem.py 
b/tests/python/tirx/operator/tile_primitive/cuda/copy/test_gmem_smem.py
index 63cad68de1..7c73261729 100644
--- a/tests/python/tirx/operator/tile_primitive/cuda/copy/test_gmem_smem.py
+++ b/tests/python/tirx/operator/tile_primitive/cuda/copy/test_gmem_smem.py
@@ -275,10 +275,6 @@ def _align(
         )
 
 
[email protected](
-    reason="align_layouts_gs ignores swizzle chunk size; "
-    "_extract_tile strips the swizzle wrap before vec_len pick."
-)
 @pytest.mark.parametrize("per_element,expected_max_vec", [(2, 4), (1, 2), (0, 
1)])
 def test_swizzled_smem_vec_len_must_fit_chunk(per_element, expected_max_vec):
     """A swizzled ``ComposeLayout`` keeps the bottom ``per_element``
@@ -538,9 +534,7 @@ def test_gmem_smem_swizzle_uses_structured_compose_apply():
         src = ex.mod.imports[0].inspect_source()
 
     s_off_lines = [
-        line
-        for line in src.splitlines()
-        if line.strip().startswith("s_off_ptr") and "[0] =" in line
+        line for line in src.splitlines() if line.strip().startswith("int 
s_off") and " = " in line
     ]
     assert len(s_off_lines) == 2, "expected one structured S offset in each 
copy direction"
     assert all("^" in line for line in s_off_lines)
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/copy/test_ld_stmatrix.py 
b/tests/python/tirx/operator/tile_primitive/cuda/copy/test_ld_stmatrix.py
index 6a359dc275..2dee76532d 100644
--- a/tests/python/tirx/operator/tile_primitive/cuda/copy/test_ld_stmatrix.py
+++ b/tests/python/tirx/operator/tile_primitive/cuda/copy/test_ld_stmatrix.py
@@ -428,7 +428,9 @@ def test_ldstmatrix_swizzle_multi_iter_pow2():
     assert "ldmatrix.sync.aligned.m8n8.x4.shared.b16" in src
 
     address_lines = [
-        line for line in src.splitlines() if "smem_off_ptr" in line and "[0] 
=" in line
+        line
+        for line in src.splitlines()
+        if line.strip().startswith("int smem_off") and " = " in line
     ]
     assert len(address_lines) == 8
     assert all("& 7) << 3" in line for line in address_lines)
@@ -499,7 +501,9 @@ def test_ldstmatrix_swizzle_multi_iter_linear():
     assert "ldmatrix.sync.aligned.m8n8.x4.shared.b16" in src
 
     address_lines = [
-        line for line in src.splitlines() if "smem_off_ptr" in line and "[0] 
=" in line
+        line
+        for line in src.splitlines()
+        if line.strip().startswith("int smem_off") and " = " in line
     ]
     assert len(address_lines) == 10
     assert all("& 7) << 3" in line for line in address_lines)
diff --git a/tests/python/tirx/operator/tile_primitive/cuda/copy/test_reg.py 
b/tests/python/tirx/operator/tile_primitive/cuda/copy/test_reg.py
index 483110858c..93eb2caf93 100644
--- a/tests/python/tirx/operator/tile_primitive/cuda/copy/test_reg.py
+++ b/tests/python/tirx/operator/tile_primitive/cuda/copy/test_reg.py
@@ -458,17 +458,43 @@ def test_reg_copy_linear_shared_hoists_thread_base():
         T.cuda.cta_sync()
         T.evaluate(smem[tid, 0])
 
+    dispatched = []
+
+    @tvm.instrument.pass_instrument
+    class CaptureDispatch:
+        def run_after_pass(self, mod, info):
+            if info.name == "tirx.TilePrimitiveDispatch":
+                dispatched.append(mod["main"])
+
     target = tvm.target.Target("cuda")
-    with target:
+    with target, tvm.transform.PassContext(instruments=[CaptureDispatch()]):
         ex = tvm.compile(tvm.IRModule({"main": kernel}), target=target, 
tir_pipeline="tirx")
         src = ex.mod.imports[0].inspect_source()
 
-    base_assignment = "s_base_ptr[0] ="
-    loop = "for (int f = 0; f < 8; ++f)"
-    assert base_assignment in src and loop in src
-    assert src.index(base_assignment) < src.index(loop)
-    assert "(s_base_ptr[0] + ds_ptr[0])" in src
-    assert "s_off_ptr" not in src
+    nodes = []
+    tvm_ffi.structural_walk(dispatched[0].body, nodes.append)
+    (copy_loop,) = [
+        node for node in nodes if isinstance(node, tvm.tirx.For) and 
int(node.extent) == 8
+    ]
+    loop_nodes = []
+    tvm_ffi.structural_walk(copy_loop.body, loop_nodes.append)
+    (pointer,) = [
+        node
+        for node in loop_nodes
+        if isinstance(node, tvm.ir.Call)
+        and node.op.name == "tirx.cuda.func_call"
+        and node.args[0].value == "tvm_builtin_pointer_offset"
+    ]
+    offset = pointer.args[2]
+    assert isinstance(offset, tvm.tirx.Add)
+    (base,) = [
+        node for node in nodes if isinstance(node, tvm.tirx.Bind) and 
node.var.same_as(offset.a)
+    ]
+    assert not any(isinstance(node, tvm.tirx.Bind) and node.same_as(base) for 
node in loop_nodes)
+    assert not any(
+        var.same_as(copy_loop.loop_var) for var in 
tvm.tirx.analysis.undefined_vars(base.value)
+    )
+    assert "st.shared.v4.u32" in src
 
 
 @pytest.mark.gpu
@@ -536,9 +562,7 @@ def 
test_reg_copy_wg_local_to_swizzled_shared_uses_structured_compose_apply():
     # (2) Structured address fingerprint: tid contributes one atom-aligned
     # add, while the bounded outer coordinate is XORed with the phase.
     s_off_lines = [
-        line
-        for line in src.splitlines()
-        if line.strip().startswith("s_off_ptr") and "[0] =" in line
+        line for line in src.splitlines() if line.strip().startswith("int 
s_off") and " = " in line
     ]
     assert len(s_off_lines) == 1
     assert "^" in s_off_lines[0]
@@ -605,6 +629,29 @@ def test_copy_fallback_handles_scalar_regions():
     assert "dst_ptr[2] = src_ptr[0];" in src
 
 
[email protected]("swizzled", [False, True])
+def test_copy_fallback_rejects_distributed_tensors(swizzled):
+    from tvm.tirx.layout import ComposeLayout
+
+    shape = (32, 4)
+    layout = TileLayout(S[shape : (1 @ tx, 1)])
+    if swizzled:
+        layout = ComposeLayout(0, 1, 2, layout)
+
+    @T.function
+    def kernel():
+        T.device_entry()
+        T.cta_id([1])
+        T.thread_id([32])
+        reg = T.alloc_tensor(shape, "float32", scope="local", layout=layout)
+        smem = T.alloc_tensor(shape, "float32", scope="shared")
+        Tx.cta.copy(smem, reg, dispatch="fallback")
+
+    with tvm.target.Target("cuda"):
+        with pytest.raises(RuntimeError, match="single-thread fallback cannot 
access"):
+            tvm.tirx.transform.LowerTIRx()(tvm.IRModule({"main": kernel}))
+
+
 @pytest.mark.gpu
 @pytest.mark.skipif(not env.has_cuda_compute(9), reason="need cuda compute >= 
9.0")
 @pytest.mark.parametrize(
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_cp.py 
b/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_cp.py
index 1b7f72c4d9..fc89f2672b 100644
--- 
a/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_cp.py
+++ 
b/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_cp.py
@@ -173,7 +173,7 @@ def _make_cp_kernel(
             tmem = T.decl_tensor(
                 t_full_shape, dtype, scope="tmem", 
allocated_addr=tmem_addr[0], layout=t_full
             )
-            if pre_zero:
+            if T.constexpr(pre_zero):
                 zero_reg = T.alloc_tensor((W32,), "uint32", scope="local")
                 for i in range(W32):
                     zero_reg[i] = T.uint32(0)
@@ -384,7 +384,7 @@ def test_cp_shape_config_routes_to_generic_planner():
     src = mod.mod.imports[0].inspect_source()
     assert "tcgen05.cp.cta_group::1.128x256b" in src
     # Generic-planner signature: descriptor template encoded at address 0...
-    assert "reinterpret_cast<void*>((uint64_t)0)" in src
+    assert "reinterpret_cast<void*>((uint64_t)0ULL)" in src
     # ...and patched per cp via the 0x3FFF address-field mask.
     assert src.count("cp_desc_ptr[0] &") == 4, f"expected 4 patched cps; 
src=\n{src}"
 
@@ -678,7 +678,7 @@ def 
test_cp_default_32x128b_instruction_sequence_unchanged():
     ]
     assert len(encode_lines) == 1, encode_lines
     # (ldo, sdo, swizzle) = (0, 8, 0), template encoded at smem address 0.
-    assert "reinterpret_cast<void*>((uint64_t)0), 0, 8, 0);" in 
encode_lines[0], encode_lines[0]
+    assert "reinterpret_cast<void*>((uint64_t)0ULL), 0, 8, 0);" in 
encode_lines[0], encode_lines[0]
 
     cp_lines = [
         line
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_ldst.py
 
b/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_ldst.py
index b355722146..6e7f3f00e2 100644
--- 
a/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_ldst.py
+++ 
b/tests/python/tirx/operator/tile_primitive/cuda/copy_async/test_tcgen05_ldst.py
@@ -742,7 +742,7 @@ def test_datapath_B_codegen(direction):
             layout=tmem_datapath_layout("B", 64, n_cols),
         )
         frag = T.alloc_tcgen05_ldst_frag("32x32b", (64, n_cols), "float32")
-        if direction == "ld":
+        if T.constexpr(direction == "ld"):
             Tx.wg.copy_async(frag[:, :], tmem[:, :])
         else:
             Tx.wg.copy_async(tmem[:, :], frag[:, :])
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_binary.py 
b/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_binary.py
index 13c1abb586..4f5894af17 100644
--- a/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_binary.py
+++ b/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_binary.py
@@ -103,19 +103,19 @@ def test_binary_op_shared(input, op_type, operands_type, 
dtype):
         Tx.cta.copy(A_smem[tuple(copy_slice)], A[tuple(copy_slice)])
         Tx.cta.copy(B_smem[tuple(copy_slice)], B[tuple(copy_slice)])
         T.cuda.cta_sync()
-        if op_type == "add":
+        if T.constexpr(op_type == "add"):
             Tx.cta.add(
                 A_smem[tuple(map_slice_res)], A_smem[tuple(map_slice_a)], 
B_smem[tuple(map_slice_b)]
             )
-        elif op_type == "sub":
+        elif T.constexpr(op_type == "sub"):
             Tx.cta.sub(
                 A_smem[tuple(map_slice_res)], A_smem[tuple(map_slice_a)], 
B_smem[tuple(map_slice_b)]
             )
-        elif op_type == "mul":
+        elif T.constexpr(op_type == "mul"):
             Tx.cta.mul(
                 A_smem[tuple(map_slice_res)], A_smem[tuple(map_slice_a)], 
B_smem[tuple(map_slice_b)]
             )
-        elif op_type == "fdiv":
+        elif T.constexpr(op_type == "fdiv"):
             Tx.cta.fdiv(
                 A_smem[tuple(map_slice_res)], A_smem[tuple(map_slice_a)], 
B_smem[tuple(map_slice_b)]
             )
@@ -134,25 +134,25 @@ def test_binary_op_shared(input, op_type, operands_type, 
dtype):
 
         Tx.cta.copy(A_smem[tuple(copy_slice)], A[tuple(copy_slice)])
         T.cuda.cta_sync()
-        if op_type == "add":
-            if operands_type == "const_region":
+        if T.constexpr(op_type == "add"):
+            if T.constexpr(operands_type == "const_region"):
                 Tx.cta.add(A_smem[tuple(map_slice_res)], const, 
A_smem[tuple(map_slice_a)])
-            elif operands_type == "region_const":
+            elif T.constexpr(operands_type == "region_const"):
                 Tx.cta.add(A_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)], const)
-        elif op_type == "sub":
-            if operands_type == "const_region":
+        elif T.constexpr(op_type == "sub"):
+            if T.constexpr(operands_type == "const_region"):
                 Tx.cta.sub(A_smem[tuple(map_slice_res)], const, 
A_smem[tuple(map_slice_a)])
-            elif operands_type == "region_const":
+            elif T.constexpr(operands_type == "region_const"):
                 Tx.cta.sub(A_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)], const)
-        elif op_type == "mul":
-            if operands_type == "const_region":
+        elif T.constexpr(op_type == "mul"):
+            if T.constexpr(operands_type == "const_region"):
                 Tx.cta.mul(A_smem[tuple(map_slice_res)], const, 
A_smem[tuple(map_slice_a)])
-            elif operands_type == "region_const":
+            elif T.constexpr(operands_type == "region_const"):
                 Tx.cta.mul(A_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)], const)
-        elif op_type == "fdiv":
-            if operands_type == "const_region":
+        elif T.constexpr(op_type == "fdiv"):
+            if T.constexpr(operands_type == "const_region"):
                 Tx.cta.fdiv(A_smem[tuple(map_slice_res)], const, 
A_smem[tuple(map_slice_a)])
-            elif operands_type == "region_const":
+            elif T.constexpr(operands_type == "region_const"):
                 Tx.cta.fdiv(A_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)], const)
         T.cuda.cta_sync()
         Tx.cta.copy(A[tuple(copy_slice)], A_smem[tuple(copy_slice)])
@@ -226,9 +226,9 @@ def test_binary_non_commutative_const_lhs_rejected(op_type):
             _bx = T.cta_id([1])
             _tid = T.thread_id([64])
             A_smem = T.alloc_tensor(shape, dtype, scope="shared", 
layout=layout)
-            if op_type == "sub":
+            if T.constexpr(op_type == "sub"):
                 Tx.cta.sub(A_smem, const, A_smem)
-            elif op_type == "fdiv":
+            elif T.constexpr(op_type == "fdiv"):
                 Tx.cta.fdiv(A_smem, const, A_smem)
 
         target = tvm.target.Target("cuda")
@@ -268,10 +268,10 @@ def test_binary_op_shared_subcta_scope(exec_scope, 
op_type):
         Tx.cta.copy(A_smem, A)
         Tx.cta.copy(B_smem, B)
         T.cuda.cta_sync()
-        if exec_scope == "warp":
+        if T.constexpr(exec_scope == "warp"):
             if warp_id == 5:
                 tx_op(A_smem, A_smem, B_smem)
-        elif exec_scope == "warpgroup":
+        elif T.constexpr(exec_scope == "warpgroup"):
             if wg_id == 1:
                 tx_op(A_smem, A_smem, B_smem)
         T.cuda.cta_sync()
@@ -309,7 +309,8 @@ def test_binary_op_local_subcta_trivial(exec_scope, 
rhs_kind, op_type):
     # in this test, use warp3/warpgroup1 to test
     thr_str = 0 if exec_scope == "cta" else (128 if exec_scope == "warpgroup" 
else 32 * 3)
     a_shape = (n_threads, m, n)
-    b_shape = (n_threads, m, n if rhs_kind == "region" else 1)
+    b_n = n if rhs_kind == "region" else 1
+    b_shape = (n_threads, m, b_n)
     c_shape = a_shape
     const = T.float16(1.25)
     tx_op = {"add": Tx.add, "sub": Tx.sub, "mul": Tx.mul, "fdiv": 
Tx.fdiv}[op_type]
@@ -329,7 +330,6 @@ def test_binary_op_local_subcta_trivial(exec_scope, 
rhs_kind, op_type):
         _bx = T.cta_id([1])
         _tid = T.thread_id([256])
         tid_in_scope = tid_in_scope_fn([n_threads])
-        b_n = T.meta_var(n if rhs_kind == "region" else 1)
         A_local = T.alloc_tensor((m, n), dtype, scope="local", 
layout=TileLayout(S[(m, n)]))
         C_local = T.alloc_tensor((m, n), dtype, scope="local", 
layout=TileLayout(S[(m, n)]))
         B_local = T.alloc_tensor((m, b_n), dtype, scope="local", 
layout=TileLayout(S[(m, b_n)]))
@@ -338,25 +338,25 @@ def test_binary_op_local_subcta_trivial(exec_scope, 
rhs_kind, op_type):
             for i in T.serial(m):
                 for j in T.serial(n):
                     A_local[i, j] = A[tid_in_scope, i, j]
-            if rhs_kind != "const":
+            if T.constexpr(rhs_kind != "const"):
                 for i in T.serial(m):
                     for j in T.serial(b_n):
                         B_local[i, j] = B[tid_in_scope, i, j]
 
-        if exec_scope == "cta":
-            if rhs_kind == "const":
+        if T.constexpr(exec_scope == "cta"):
+            if T.constexpr(rhs_kind == "const"):
                 tx_op(C_local, A_local, const)
             else:
                 tx_op(C_local, A_local, B_local)
-        elif exec_scope == "warpgroup":
+        elif T.constexpr(exec_scope == "warpgroup"):
             if wg_id == 1:
-                if rhs_kind == "const":
+                if T.constexpr(rhs_kind == "const"):
                     tx_op(C_local, A_local, const)
                 else:
                     tx_op(C_local, A_local, B_local)
         else:
             if warp_id == 3:
-                if rhs_kind == "const":
+                if T.constexpr(rhs_kind == "const"):
                     tx_op(C_local, A_local, const)
                 else:
                     tx_op(C_local, A_local, B_local)
@@ -437,7 +437,7 @@ def test_binary_op_vectorized(input, storage_scope, 
exec_scope, op_type, dtype):
         T.device_entry()
         _bx = T.cta_id([1])
         tx = T.thread_id([thread_cnt])
-        if storage_scope == "shared":
+        if T.constexpr(storage_scope == "shared"):
             A_smem = T.alloc_tensor(a_shape, dtype, scope="shared", 
layout=TileLayout(S[a_shape]))
             B_smem = T.alloc_tensor(b_shape, dtype, scope="shared", 
layout=TileLayout(S[b_shape]))
             Tx.cta.copy(A_smem, A)
@@ -446,7 +446,7 @@ def test_binary_op_vectorized(input, storage_scope, 
exec_scope, op_type, dtype):
             tx_op(A_smem, A_smem, B_smem)
             T.cuda.cta_sync()
             Tx.cta.copy(A, A_smem)
-        if storage_scope == "local":
+        if T.constexpr(storage_scope == "local"):
             A_local = T.alloc_tensor(
                 a_shape[1:], dtype, scope="local", 
layout=TileLayout(S[a_shape[1:]])
             )
@@ -467,7 +467,7 @@ def test_binary_op_vectorized(input, storage_scope, 
exec_scope, op_type, dtype):
         T.device_entry()
         _bx = T.cta_id([1])
         tx = T.thread_id([thread_cnt])
-        if storage_scope == "shared":
+        if T.constexpr(storage_scope == "shared"):
             A_smem = T.alloc_tensor(a_shape, dtype, scope="shared", 
layout=TileLayout(S[a_shape]))
             B_smem = T.alloc_tensor(b_shape, dtype, scope="shared", 
layout=TileLayout(S[b_shape]))
             Tx.copy(A_smem, A)
@@ -476,7 +476,7 @@ def test_binary_op_vectorized(input, storage_scope, 
exec_scope, op_type, dtype):
             tx_op(A_smem, A_smem, B_smem)
             T.cuda.cta_sync()
             Tx.copy(A, A_smem)
-        elif storage_scope == "local":
+        elif T.constexpr(storage_scope == "local"):
             A_local = T.alloc_tensor(
                 a_shape[1:], dtype, scope="local", 
layout=TileLayout(S[a_shape[1:]])
             )
@@ -553,11 +553,11 @@ def test_binary_op_packed_f32x2_auto_dispatch(op_type):
         )
         Tx.copy(A_local, A[tx])
         Tx.copy(B_local, B[tx])
-        if op_type == "add":
+        if T.constexpr(op_type == "add"):
             Tx.add(A_local, A_local, B_local)
-        elif op_type == "sub":
+        elif T.constexpr(op_type == "sub"):
             Tx.sub(A_local, A_local, B_local)
-        elif op_type == "mul":
+        elif T.constexpr(op_type == "mul"):
             Tx.mul(A_local, A_local, B_local)
         Tx.copy(A[tx], A_local)
 
@@ -620,11 +620,11 @@ def test_binary_op_warpgroup_wg_local_layout(op_name):
             lhs_row[i] = A[tid, i]
             rhs_row[i] = B[tid, i]
             out_row[i] = T.float32(0)
-        if op_name == "add":
+        if T.constexpr(op_name == "add"):
             Tx.wg.add(out, lhs, rhs)
-        elif op_name == "sub":
+        elif T.constexpr(op_name == "sub"):
             Tx.wg.sub(out, lhs, rhs)
-        elif op_name == "mul":
+        elif T.constexpr(op_name == "mul"):
             Tx.wg.mul(out, lhs, rhs)
         out_row_1 = out.local(cols)
         for i in T.serial(cols):
@@ -697,9 +697,9 @@ def 
test_binary_op_warpgroup_wg_local_emits_packed_f32x2(op_name, ptx_op):
             lhs_row[i] = A[tid, i]
             rhs_row[i] = B[tid, i]
             out_row[i] = T.float32(0)
-        if op_name == "add":
+        if T.constexpr(op_name == "add"):
             Tx.wg.add(out, lhs, rhs)
-        elif op_name == "sub":
+        elif T.constexpr(op_name == "sub"):
             Tx.wg.sub(out, lhs, rhs)
         else:
             Tx.wg.mul(out, lhs, rhs)
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_unary.py 
b/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_unary.py
index 4f2e415d40..9849d812ca 100644
--- a/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_unary.py
+++ b/tests/python/tirx/operator/tile_primitive/cuda/elementwise/test_unary.py
@@ -82,9 +82,9 @@ def test_unary_op_shared(input, op_type, src_dtype, 
dst_dtype):
             A_smem = T.alloc_tensor(s_shape, src_dtype, scope="shared", 
layout=s_layout)
             Tx.cta.copy(A_smem[tuple(copy_slice)], A[tuple(copy_slice)])
             T.cuda.cta_sync()
-            if op_type == "zero":
+            if T.constexpr(op_type == "zero"):
                 Tx.cta.zero(A_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)])
-            elif op_type == "sqrt":
+            elif T.constexpr(op_type == "sqrt"):
                 Tx.cta.sqrt(A_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)])
             T.cuda.cta_sync()
             Tx.cta.copy(A[tuple(copy_slice)], A_smem[tuple(copy_slice)])
@@ -104,9 +104,9 @@ def test_unary_op_shared(input, op_type, src_dtype, 
dst_dtype):
             B_smem = T.alloc_tensor(s_shape, dst_dtype, scope="shared", 
layout=s_layout)
             Tx.cta.copy(A_smem[tuple(copy_slice)], A[tuple(copy_slice)])
             T.cuda.cta_sync()
-            if op_type == "zero":
+            if T.constexpr(op_type == "zero"):
                 Tx.cta.zero(B_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)])
-            elif op_type == "sqrt":
+            elif T.constexpr(op_type == "sqrt"):
                 Tx.cta.sqrt(B_smem[tuple(map_slice_res)], 
A_smem[tuple(map_slice_a)])
             T.cuda.cta_sync()
             Tx.cta.copy(B[tuple(map_slice_res)], B_smem[tuple(map_slice_res)])
@@ -169,10 +169,10 @@ def test_unary_op_shared_subcta_scope(exec_scope):
         A_smem = T.alloc_tensor(g_shape, dtype, scope="shared", 
layout=TileLayout(S[g_shape]))
         Tx.cta.copy(A_smem, A)
         T.cuda.cta_sync()
-        if exec_scope == "warp":
+        if T.constexpr(exec_scope == "warp"):
             if warp_id == 5:
                 Tx.warp.zero(A_smem, A_smem)
-        elif exec_scope == "warpgroup":
+        elif T.constexpr(exec_scope == "warpgroup"):
             if wg_id == 1:
                 Tx.wg.zero(A_smem, A_smem)
         T.cuda.cta_sync()
@@ -261,30 +261,30 @@ def test_unary_op_shared_with_bias_scale(input, op_type, 
bias_type, src_dtype, d
             Tx.cta.copy(A_smem[tuple(copy_slice)], A[tuple(copy_slice)])
             Tx.cta.copy(bias_smem[tuple(copy_slice)], bias[tuple(copy_slice)])
             T.cuda.cta_sync()
-            if bias_type == "const":
-                if op_type == "sqrt":
+            if T.constexpr(bias_type == "const"):
+                if T.constexpr(op_type == "sqrt"):
                     Tx.cta.sqrt_with_scale_bias(
                         A_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
                         scale=scale,
                         bias=const_bias,
                     )
-                elif op_type == "exp":
+                elif T.constexpr(op_type == "exp"):
                     Tx.cta.exp_with_scale_bias(
                         A_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
                         scale=scale,
                         bias=const_bias,
                     )
-            elif bias_type == "region":
-                if op_type == "sqrt":
+            elif T.constexpr(bias_type == "region"):
+                if T.constexpr(op_type == "sqrt"):
                     Tx.cta.sqrt_with_scale_bias(
                         A_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
                         scale=scale,
                         bias=bias_smem[tuple(map_slice_a)],
                     )
-                elif op_type == "exp":
+                elif T.constexpr(op_type == "exp"):
                     Tx.cta.exp_with_scale_bias(
                         A_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
@@ -310,30 +310,30 @@ def test_unary_op_shared_with_bias_scale(input, op_type, 
bias_type, src_dtype, d
             Tx.cta.copy(A_smem[tuple(copy_slice)], A[tuple(copy_slice)])
             Tx.cta.copy(bias_smem[tuple(copy_slice)], bias[tuple(copy_slice)])
             T.cuda.cta_sync()
-            if bias_type == "const":
-                if op_type == "sqrt":
+            if T.constexpr(bias_type == "const"):
+                if T.constexpr(op_type == "sqrt"):
                     Tx.cta.sqrt_with_scale_bias(
                         B_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
                         scale=scale,
                         bias=const_bias,
                     )
-                elif op_type == "exp":
+                elif T.constexpr(op_type == "exp"):
                     Tx.cta.exp_with_scale_bias(
                         B_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
                         scale=scale,
                         bias=const_bias,
                     )
-            elif bias_type == "region":
-                if op_type == "sqrt":
+            elif T.constexpr(bias_type == "region"):
+                if T.constexpr(op_type == "sqrt"):
                     Tx.cta.sqrt_with_scale_bias(
                         B_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
                         scale=scale,
                         bias=bias_smem[tuple(map_slice_a)],
                     )
-                elif op_type == "exp":
+                elif T.constexpr(op_type == "exp"):
                     Tx.cta.exp_with_scale_bias(
                         B_smem[tuple(map_slice_res)],
                         A_smem[tuple(map_slice_a)],
@@ -502,13 +502,13 @@ def test_unary_op_local(input, op_type, src_dtype, 
dst_dtype):
             # unary op
         acc_view = acc.view(*acc_shape, layout=acc_layout)
         res_view = res.view(*red_shape, layout=acc_layout)
-        if op_type == "reciprocal":
+        if T.constexpr(op_type == "reciprocal"):
             Tx.warp.reciprocal(res_view, acc_view)
-        elif op_type == "exp":
+        elif T.constexpr(op_type == "exp"):
             Tx.warp.exp(res_view, acc_view)
-        elif op_type == "exp2":
+        elif T.constexpr(op_type == "exp2"):
             Tx.warp.exp2(res_view, acc_view)
-        elif op_type == "log2":
+        elif T.constexpr(op_type == "log2"):
             Tx.warp.log2(res_view, acc_view)
 
             # write res into B
@@ -655,15 +655,15 @@ def test_unary_op_local_with_bias_scale(input, op_type, 
bias_type, src_dtype, ds
         acc_view = acc.view(*acc_shape, layout=acc_layout)
         res_view = res.view(*red_shape, layout=acc_layout)
         bias_view = bias_local.view(*bias_shape, layout=acc_layout)
-        if bias_type == "const":
-            if op_type == "sqrt":
+        if T.constexpr(bias_type == "const"):
+            if T.constexpr(op_type == "sqrt"):
                 Tx.warp.sqrt_with_scale_bias(res_view, acc_view, scale=scale, 
bias=const_bias)
-            elif op_type == "exp":
+            elif T.constexpr(op_type == "exp"):
                 Tx.warp.exp_with_scale_bias(res_view, acc_view, scale=scale, 
bias=const_bias)
-        elif bias_type == "region":
-            if op_type == "sqrt":
+        elif T.constexpr(bias_type == "region"):
+            if T.constexpr(op_type == "sqrt"):
                 Tx.warp.sqrt_with_scale_bias(res_view, acc_view, scale=scale, 
bias=bias_view)
-            elif op_type == "exp":
+            elif T.constexpr(op_type == "exp"):
                 Tx.warp.exp_with_scale_bias(res_view, acc_view, scale=scale, 
bias=bias_view)
 
             # write res into B
@@ -733,13 +733,13 @@ def test_unary_op_vectorized(shape, op_type, exec_scope, 
storage_scope):
         T.device_entry()
         _bx = T.cta_id([1])
         tx = T.thread_id([128])
-        if storage_scope == "shared":
+        if T.constexpr(storage_scope == "shared"):
             a_smem = T.alloc_tensor(
                 shape, dtype=dtype, layout=TileLayout(S[shape]), scope="shared"
             )
             Tx.fill(a_smem[tx], value)
             Tx.copy(A[tx], a_smem[tx])
-        elif storage_scope == "local":
+        elif T.constexpr(storage_scope == "local"):
             a_local = T.alloc_tensor(
                 shape[1:], dtype=dtype, layout=TileLayout(S[shape[1:]]), 
scope="local"
             )
@@ -752,7 +752,7 @@ def test_unary_op_vectorized(shape, op_type, exec_scope, 
storage_scope):
         T.device_entry()
         _bx = T.cta_id([1])
         _tid = T.thread_id([128])
-        if storage_scope == "shared":
+        if T.constexpr(storage_scope == "shared"):
             a_smem = T.alloc_tensor(
                 shape, dtype=dtype, layout=TileLayout(S[shape]), scope="shared"
             )
@@ -794,15 +794,15 @@ def test_unary_op_local_thread_wise(op_type, dtype):
             local_shape, dtype, scope="local", 
layout=TileLayout(S[local_shape])
         )
         Tx.copy(a_local, A[tid])
-        if op_type == "zero":
+        if T.constexpr(op_type == "zero"):
             Tx.zero(a_local, a_local)
-        elif op_type == "sqrt":
+        elif T.constexpr(op_type == "sqrt"):
             Tx.sqrt(a_local, a_local)
-        elif op_type == "reciprocal":
+        elif T.constexpr(op_type == "reciprocal"):
             Tx.reciprocal(a_local, a_local)
-        elif op_type == "exp":
+        elif T.constexpr(op_type == "exp"):
             Tx.exp(a_local, a_local)
-        elif op_type == "silu":
+        elif T.constexpr(op_type == "silu"):
             Tx.silu(a_local, a_local)
         Tx.copy(A[tid], a_local)
 
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/gemm/test_gemm_mma_m16n8k_.py 
b/tests/python/tirx/operator/tile_primitive/cuda/gemm/test_gemm_mma_m16n8k_.py
index 214f2cc354..ce68054fbb 100644
--- 
a/tests/python/tirx/operator/tile_primitive/cuda/gemm/test_gemm_mma_m16n8k_.py
+++ 
b/tests/python/tirx/operator/tile_primitive/cuda/gemm/test_gemm_mma_m16n8k_.py
@@ -33,6 +33,7 @@ is guarded by ``requires_cuda`` since it needs a real device.
 
 import numpy as np
 import pytest
+from tvm_ffi import structural_walk
 
 import tvm
 import tvm.testing
@@ -278,7 +279,7 @@ def _build_tiled_numeric(Mt, Nt, Kt, kinst, beta, dtype):
                 kt * kinst + kHi * 8 + 2 * (lane % 4) + kp,
                 nt * 8 + lane // 4,
             ]
-        if beta == 1.0:
+        if T.constexpr(beta == 1.0):
             C_reg = C_f.local(Mt, Nt, 2, 2)
             for mt, nt, rM, rN in T.grid(Mt, Nt, 2, 2):
                 C_reg[mt, nt, rM, rN] = C_g[
@@ -318,7 +319,7 @@ def _build_transpose_numeric(transpose_A, transpose_B, 
dtype="float16"):
         B_f = T.alloc_tensor(B_shape, dtype, scope="local", layout=Bl)
         D_f = T.alloc_tensor((16, 8), "float32", scope="local", layout=D_FRAG)
         A_reg = A_f.local(2, 2, 2)
-        if transpose_A:
+        if T.constexpr(transpose_A):
             # A_KM_FRAG: buffer is [K, M].
             for kHi, rM, kp in T.grid(2, 2, 2):
                 A_reg[kHi, rM, kp] = A_g[2 * (lane % 4) + kp + 8 * kHi, lane 
// 4 + 8 * rM]
@@ -327,7 +328,7 @@ def _build_transpose_numeric(transpose_A, transpose_B, 
dtype="float16"):
             for kHi, rM, kp in T.grid(2, 2, 2):
                 A_reg[kHi, rM, kp] = A_g[lane // 4 + 8 * rM, 2 * (lane % 4) + 
kp + 8 * kHi]
         B_reg = B_f.local(2, 2)
-        if transpose_B:
+        if T.constexpr(transpose_B):
             # B_NK_FRAG buffer is [N, K].
             for kHi, kp in T.grid(2, 2):
                 B_reg[kHi, kp] = B_g[lane // 4, 2 * (lane % 4) + kp + 8 * kHi]
@@ -372,35 +373,51 @@ def test_cuda_gemm_mma_variant_is_registered():
 def test_cuda_gemm_mma_lowers_to_mma_sync(dtype):
     """beta=0: the dispatch clears D, then issues a single accumulating mma 
with
     the registers laid out in the fixed PTX fragment order."""
-    script = _lower(_build_gemm(alpha=1.0, beta=0.0, 
dtype=dtype))["main"].script()
-
-    assert "T.ptx.mma(" in script
-    assert "m16n8k16" in script
+    func = _lower(_build_gemm(alpha=1.0, beta=0.0, dtype=dtype))["main"]
+    calls, stores = [], []
+    structural_walk(func.body, [(tvm.ir.Call, calls.append), 
(tvm.tirx.TensorStore, stores.append)])
+    mma_calls = [call for call in calls if call.op.name == "tirx.ptx.mma"]
+    assert len(mma_calls) == 1
+    operands = mma_calls[0].args
+    assert operands[16].value == "m16n8k16"
     # beta == 0 clears the accumulator before the K loop.
-    assert "T.float32(0" in script
+    assert len(stores) == 1
+    assert isinstance(stores[0].value, tvm.tirx.FloatImm) and 
stores[0].value.value == 0
+    assert stores[0].buffer.same_as(operands[0].source)
     # D accumulator: c_id = 2*rM + rN -> regs 0..3.
-    for r in range(4):
-        assert f"d_local[{r}]" in script
     # A and B fragments are packed two elements per b32, so the instruction
     # indexes a uint32 view: the element strides above halve into word strides.
     # A: b32 = rM + 2*kHi (kHi outer) -> words 0..3.
-    for r in range(4):
-        assert f"a_words[{r}]" in script
     # B: b32 = kHi -> words 0, 1.
-    for r in (0, 1):
-        assert f"b_words[{r}]" in script
+    for fragment, dtype in [
+        (operands[:4], "float32"),
+        (operands[4:8], "uint32"),
+        (operands[8:10], "uint32"),
+    ]:
+        for r, load in enumerate(fragment):
+            assert isinstance(load, tvm.ir.TensorLoad)
+            assert load.source.same_as(fragment[0].source)
+            assert load.ty.dtype == dtype
+            assert len(load.indices) == 1 and int(load.indices[0]) == r
+    for dst, src in zip(operands[:4], operands[10:14]):
+        tvm.ir.assert_structural_equal(dst, src)
 
 
 @pytest.mark.gpu
 def test_cuda_gemm_mma_accumulates_c_when_beta_one():
     """beta=1: the accumulator is initialized by copying C instead of 
zeroing."""
-    script = _lower(_build_gemm(alpha=1.0, beta=1.0))["main"].script()
-
-    assert "T.ptx.mma(" in script
-    assert "m16n8k16" in script
+    func = _lower(_build_gemm(alpha=1.0, beta=1.0))["main"]
+    calls, stores = [], []
+    structural_walk(func.body, [(tvm.ir.Call, calls.append), 
(tvm.tirx.TensorStore, stores.append)])
+    mma_calls = [call for call in calls if call.op.name == "tirx.ptx.mma"]
+    assert len(mma_calls) == 1
     # The init reads C into D; nothing is zeroed.
-    assert "c_local[" in script
-    assert "T.float32(0" not in script
+    assert len(stores) == 1
+    init = stores[0]
+    assert init.buffer.same_as(mma_calls[0].args[0].source)
+    assert isinstance(init.value, tvm.ir.TensorLoad)
+    assert not init.buffer.same_as(init.value.source)
+    tvm.ir.assert_structural_equal(init.indices, init.value.indices)
 
 
 def test_cuda_gemm_mma_rejects_nonunit_alpha():
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/gemm_async/test_gemm_async.py 
b/tests/python/tirx/operator/tile_primitive/cuda/gemm_async/test_gemm_async.py
index 7c1540c266..7581d3303f 100644
--- 
a/tests/python/tirx/operator/tile_primitive/cuda/gemm_async/test_gemm_async.py
+++ 
b/tests/python/tirx/operator/tile_primitive/cuda/gemm_async/test_gemm_async.py
@@ -2978,7 +2978,7 @@ def _build_smem_desc_kernel(smem_desc, 
weight_stationary=False, pass_descI=False
         T.cuda.mbarrier_wait(tma_mbar.ptr_to([0]), 0)
         T.cuda.cta_sync()
         if tid_in_wg == 0:
-            if pass_descI:
+            if T.constexpr(pass_descI):
                 desc_i: T.uint32
                 T.cuda.tcgen05.encode_instr_descriptor(
                     T.address_of(desc_i),  # noqa: F821
diff --git 
a/tests/python/tirx/operator/tile_primitive/cuda/reduction/test_reduction.py 
b/tests/python/tirx/operator/tile_primitive/cuda/reduction/test_reduction.py
index 3da84058b6..2a18e42448 100644
--- a/tests/python/tirx/operator/tile_primitive/cuda/reduction/test_reduction.py
+++ b/tests/python/tirx/operator/tile_primitive/cuda/reduction/test_reduction.py
@@ -81,24 +81,24 @@ def test_reduction_shared(
         B_smem = T.alloc_tensor(s_shape_dst, dtype, scope="shared", 
layout=s_layout_dst)
 
         Tx.cta.copy(A_smem[tuple(copy_slice_src)], A[tuple(copy_slice_src)])
-        if accum:
+        if T.constexpr(accum):
             Tx.cta.copy(B_smem[tuple(copy_slice_dst)], 
B[tuple(copy_slice_dst)])
         T.cuda.cta_sync()
-        if op_type == "sum":
+        if T.constexpr(op_type == "sum"):
             Tx.cta.sum(
                 B_smem[tuple(reduce_slice_dst)],
                 A_smem[tuple(reduce_slice_src)],
                 axes=axes,
                 accum=accum,
             )
-        elif op_type == "max":
+        elif T.constexpr(op_type == "max"):
             Tx.cta.max(
                 B_smem[tuple(reduce_slice_dst)],
                 A_smem[tuple(reduce_slice_src)],
                 axes=axes,
                 accum=accum,
             )
-        elif op_type == "min":
+        elif T.constexpr(op_type == "min"):
             Tx.cta.min(
                 B_smem[tuple(reduce_slice_dst)],
                 A_smem[tuple(reduce_slice_src)],
@@ -181,15 +181,15 @@ def test_reduction_shared_subscope(exec_scope, op_type, 
accum):
             A_smem = T.alloc_tensor(list(src_shape), dtype, scope="shared", 
layout=s_layout_src)
             B_smem = T.alloc_tensor(list(dst_shape), dtype, scope="shared", 
layout=s_layout_dst)
             Tx.cta.copy(A_smem, A)
-            if accum:
+            if T.constexpr(accum):
                 Tx.cta.copy(B_smem, B)
             T.cuda.cta_sync()
             if warp_id == 5:
-                if op_type == "sum":
+                if T.constexpr(op_type == "sum"):
                     Tx.warp.sum(B_smem, A_smem, axes=axes, accum=accum)
-                elif op_type == "max":
+                elif T.constexpr(op_type == "max"):
                     Tx.warp.max(B_smem, A_smem, axes=axes, accum=accum)
-                elif op_type == "min":
+                elif T.constexpr(op_type == "min"):
                     Tx.warp.min(B_smem, A_smem, axes=axes, accum=accum)
             T.cuda.cta_sync()
             Tx.cta.copy(B, B_smem)
@@ -208,15 +208,15 @@ def test_reduction_shared_subscope(exec_scope, op_type, 
accum):
             A_smem = T.alloc_tensor(list(src_shape), dtype, scope="shared", 
layout=s_layout_src)
             B_smem = T.alloc_tensor(list(dst_shape), dtype, scope="shared", 
layout=s_layout_dst)
             Tx.cta.copy(A_smem, A)
-            if accum:
+            if T.constexpr(accum):
                 Tx.cta.copy(B_smem, B)
             T.cuda.cta_sync()
             if wg_id == 0:
-                if op_type == "sum":
+                if T.constexpr(op_type == "sum"):
                     Tx.wg.sum(B_smem, A_smem, axes=axes, accum=accum)
-                elif op_type == "max":
+                elif T.constexpr(op_type == "max"):
                     Tx.wg.max(B_smem, A_smem, axes=axes, accum=accum)
-                elif op_type == "min":
+                elif T.constexpr(op_type == "min"):
                     Tx.wg.min(B_smem, A_smem, axes=axes, accum=accum)
             T.cuda.cta_sync()
             Tx.cta.copy(B, B_smem)
@@ -234,15 +234,15 @@ def test_reduction_shared_subscope(exec_scope, op_type, 
accum):
             A_smem = T.alloc_tensor(list(src_shape), dtype, scope="shared", 
layout=s_layout_src)
             B_smem = T.alloc_tensor(list(dst_shape), dtype, scope="shared", 
layout=s_layout_dst)
             Tx.cta.copy(A_smem, A)
-            if accum:
+            if T.constexpr(accum):
                 Tx.cta.copy(B_smem, B)
             T.cuda.cta_sync()
             if _tid == 65:
-                if op_type == "sum":
+                if T.constexpr(op_type == "sum"):
                     Tx.sum(B_smem, A_smem, axes=axes, accum=accum)
-                elif op_type == "max":
+                elif T.constexpr(op_type == "max"):
                     Tx.max(B_smem, A_smem, axes=axes, accum=accum)
-                elif op_type == "min":
+                elif T.constexpr(op_type == "min"):
                     Tx.min(B_smem, A_smem, axes=axes, accum=accum)
             T.cuda.cta_sync()
             Tx.cta.copy(B, B_smem)
@@ -339,16 +339,16 @@ def test_reduction_local_thread_wise(src_shape, 
dst_shape, axes, op_type, accum)
             idx = T.meta_var(decompose_flat(i, src_shape))
             A_local[tuple(idx)] = A[tuple(idx)]
 
-        if accum:
+        if T.constexpr(accum):
             for i in T.serial(dst_total):
                 idx = T.meta_var(decompose_flat(i, dst_shape))
                 B_local[tuple(idx)] = B[tuple(idx)]
 
-        if op_type == "sum":
+        if T.constexpr(op_type == "sum"):
             Tx.sum(B_local, A_local, axes=axes, accum=accum)
-        elif op_type == "max":
+        elif T.constexpr(op_type == "max"):
             Tx.max(B_local, A_local, axes=axes, accum=accum)
-        elif op_type == "min":
+        elif T.constexpr(op_type == "min"):
             Tx.min(B_local, A_local, axes=axes, accum=accum)
 
         for i in T.serial(dst_total):
@@ -468,31 +468,31 @@ def test_reduction_local_view_basic(inner_dims, dst_dims, 
axes, accum, slice_end
         for i in T.serial(src_local_total):
             idx = T.meta_var(decompose_flat(i, inner_dims))
             acc[(0, *list(idx))] = A[(lane_id, *list(idx))]
-        if accum:
+        if T.constexpr(accum):
             for i in T.serial(dst_local_total):
                 idx = T.meta_var(decompose_flat(i, dst_dims))
                 red[(0, *list(idx))] = B[(lane_id, *list(idx))]
         acc_view = acc.view(*src_shape, layout=acc_view_layout)
         red_view = red.view(*dst_shape, layout=red_view_layout)
-        if slice_end is not None:
-            if op_type == "sum":
+        if T.constexpr(slice_end is not None):
+            if T.constexpr(op_type == "sum"):
                 Tx.warp.sum(
                     red_view, acc_view[:, slice_end // 2 : slice_end], 
axes=axes, accum=accum
                 )
-            elif op_type == "max":
+            elif T.constexpr(op_type == "max"):
                 Tx.warp.max(
                     red_view, acc_view[:, slice_end // 2 : slice_end], 
axes=axes, accum=accum
                 )
-            elif op_type == "min":
+            elif T.constexpr(op_type == "min"):
                 Tx.warp.min(
                     red_view, acc_view[:, slice_end // 2 : slice_end], 
axes=axes, accum=accum
                 )
         else:
-            if op_type == "sum":
+            if T.constexpr(op_type == "sum"):
                 Tx.warp.sum(red_view, acc_view, axes=axes, accum=accum)
-            elif op_type == "max":
+            elif T.constexpr(op_type == "max"):
                 Tx.warp.max(red_view, acc_view, axes=axes, accum=accum)
-            elif op_type == "min":
+            elif T.constexpr(op_type == "min"):
                 Tx.warp.min(red_view, acc_view, axes=axes, accum=accum)
         for i in T.serial(dst_local_total):
             idx = T.meta_var(decompose_flat(i, dst_dims))
@@ -599,7 +599,7 @@ def test_reduction_local_view_complex(n_groups, n_warps, 
op_type, dtype, shuffle
                     ]
 
             # Pre-load B into red for accumulation
-        if accum:
+        if T.constexpr(accum):
             for i in T.unroll(2):
                 red[i] = B[
                     wg_id * 64 + warp_id_in_wg * 16 + i * 8 + lane_id // 4,
@@ -607,19 +607,19 @@ def test_reduction_local_view_complex(n_groups, n_warps, 
op_type, dtype, shuffle
                 ]
         acc_view = acc.view(*acc_shape, layout=acc_layout)
         red_view = red.view(*red_shape, layout=red_layout)
-        if op_type == "sum":
+        if T.constexpr(op_type == "sum"):
             Tx.warp.sum(red_view, acc_view, thread_reduce=shuffle, accum=accum)
-        elif op_type == "max":
+        elif T.constexpr(op_type == "max"):
             Tx.warp.max(red_view, acc_view, thread_reduce=shuffle, accum=accum)
-        elif op_type == "min":
+        elif T.constexpr(op_type == "min"):
             Tx.warp.min(red_view, acc_view, thread_reduce=shuffle, accum=accum)
             # perform an additional shuffle step if not shuffled above
-        if not shuffle:
-            if op_type == "sum":
+        if T.constexpr(not shuffle):
+            if T.constexpr(op_type == "sum"):
                 Tx.warp.sum(red_view, red_view, thread_reduce=True)
-            elif op_type == "max":
+            elif T.constexpr(op_type == "max"):
                 Tx.warp.max(red_view, red_view, thread_reduce=True)
-            elif op_type == "min":
+            elif T.constexpr(op_type == "min"):
                 Tx.warp.min(red_view, red_view, thread_reduce=True)
             # Write red into B
         for i in T.unroll(2):
@@ -698,13 +698,13 @@ def 
test_reduction_local_optimized_3input_maxmin(reduction_len, op_type, accum):
             A_local[i] = A[i]
 
             # Initialize B_local for accum test
-        if accum:
+        if T.constexpr(accum):
             B_local[0] = B[0]
 
             # Thread-level reduction
-        if op_type == "max":
+        if T.constexpr(op_type == "max"):
             Tx.max(B_local, A_local, accum=accum)
-        elif op_type == "min":
+        elif T.constexpr(op_type == "min"):
             Tx.min(B_local, A_local, accum=accum)
 
             # Store result to global
@@ -771,7 +771,7 @@ def 
test_reduction_local_optimized_packed_add_sum(reduction_len, accum):
             A_local[i] = A[i]
 
             # Initialize B_local for accum test
-        if accum:
+        if T.constexpr(accum):
             B_local[0] = B[0]
 
             # Thread-level sum reduction
@@ -845,9 +845,9 @@ def test_reduction_op_warp_shuffle(op_type, dtype):
         src_local[0] = A[lane_id]
         src_view = src_local.view(N, layout=src_layout)
         dst_view = dst_local.view(1, layout=dst_layout)
-        if op_type == "sum":
+        if T.constexpr(op_type == "sum"):
             Tx.warp.sum(dst_view, src_view)
-        elif op_type == "max":
+        elif T.constexpr(op_type == "max"):
             Tx.warp.max(dst_view, src_view)
         B[lane_id] = dst_local[0]
         # fmt: on
@@ -917,9 +917,9 @@ def test_reduction_op_warp_shuffle_multi_elem(op_type, 
dtype):
             src_local[i] = A[lane_id * ELEMS_PER_THREAD + i]
         src_view = src_local.view(TOTAL, layout=src_layout)
         dst_view = dst_local.view(ELEMS_PER_THREAD, layout=dst_layout)
-        if op_type == "sum":
+        if T.constexpr(op_type == "sum"):
             Tx.warp.sum(dst_view, src_view)
-        elif op_type == "max":
+        elif T.constexpr(op_type == "max"):
             Tx.warp.max(dst_view, src_view)
         for i in T.serial(ELEMS_PER_THREAD):
             B[i] = dst_local[i]
@@ -1115,7 +1115,7 @@ def test_reduction_warpgroup_wg_local_layout(op_name):
         src_local = src.local(cols)
         for i in T.serial(cols):
             src_local[i] = A[tid, i]
-        if op_name == "sum":
+        if T.constexpr(op_name == "sum"):
             Tx.wg.sum(dst, src, axes=[-1], accum=False)
         else:
             Tx.wg.max(dst, src, axes=[-1], accum=False)
diff --git a/tests/python/tirx/test_op_namespace_cleanup.py 
b/tests/python/tirx/test_op_namespace_cleanup.py
index f5ffce8333..33cc63a5fd 100644
--- a/tests/python/tirx/test_op_namespace_cleanup.py
+++ b/tests/python/tirx/test_op_namespace_cleanup.py
@@ -105,6 +105,36 @@ def test_builtin_expression_ops_are_not_tile_primitives():
     assert fma.op.name == "tirx.fma"
 
 
+def test_tensormap_encoding_accepts_attribute_keywords_and_defaults():
+    @T.function
+    def encode(descriptor: T.TensorMap, data: T.handle):
+        T.tensormap_encode_tiled(
+            descriptor,
+            data,
+            64,
+            32,
+            128,
+            64,
+            32,
+            1,
+            1,
+            descriptor_dtype="bfloat16",
+            rank=2,
+            swizzle=3,
+        )
+
+    (call,) = [c for c in _expr_calls(encode) if c.op.name == 
"tirx.tensormap_encode_tiled"]
+    assert call.op.attrs_type_key == "tirx.TensorMapEncodeTiledAttr"
+    assert str(call.attrs.descriptor_dtype) == "bfloat16"
+    assert call.attrs.rank == 2
+    assert call.attrs.swizzle == 3
+    assert call.attrs.interleave == call.attrs.l2_promotion == 
call.attrs.oob_fill == 0
+    assert call.attrs.force_cu_dtype == -1
+    call.validate()
+    reparsed = tvm.script.from_source(encode.script(), extra_vars={"T": T})
+    assert_structural_equal(encode, reparsed)
+
+
 def test_kernel_replace_point_is_builtin_marker_not_tile_primitive():
     assert _op_attr("tirx.tvm_kernel_replace_point", "TIRxOpCategory") == 
"builtin"
     assert "tirx.tile.tvm_kernel_replace_point" not in Op.list_op_names()
diff --git a/tests/python/tirx/transform/test_transform_lower_tirx.py 
b/tests/python/tirx/transform/test_transform_lower_tirx.py
index e0c60d951e..5d54c99ae2 100644
--- a/tests/python/tirx/transform/test_transform_lower_tirx.py
+++ b/tests/python/tirx/transform/test_transform_lower_tirx.py
@@ -1439,6 +1439,36 @@ def 
test_lower_exec_context_tracks_cta_id_in_pair_after_axis_predicate():
     assert _int_triple(seen[0], "cby") == (1, 1, 2)
 
 
[email protected]("offset", [0, 16])
+def test_lower_remaps_tensor_memory_address_metadata(offset):
+    @T.function(private=True)
+    def before():
+        T.device_entry()
+        addr = T.alloc_shared((1,), "uint32")
+        for i in T.serial(2):
+            tensor = T.decl_tensor(
+                (64,),
+                "float32",
+                scope="tmem",
+                allocated_addr=addr[0] + T.uint32(offset) + T.cast(i, 
"uint32"),
+            )
+            T.evaluate(tensor.data)
+
+    with tvm.target.Target("cuda"):
+        lowered = LowerTIRx()(tvm.IRModule({"main": before}))["main"]
+    assert not tvm.tirx.analysis.undefined_vars(lowered.body, lowered.params)
+    tvm.tirx.analysis.verify_well_formed(lowered)
+    bindings = []
+    tvm_ffi.structural_walk(lowered.body, (tvm.tirx.Bind, bindings.append))
+    address = next(node.var for node in bindings if node.var.scope() == 
"shared")
+    tensor = next(node for node in bindings if node.var.scope() == "tmem")
+    loads = []
+    tvm_ffi.structural_walk(tensor.var.ty.allocated_addr, (tvm.ir.TensorLoad, 
loads.append))
+    assert len(loads) == 1
+    assert loads[0].source.same_as(address)
+    tvm.ir.assert_structural_equal(tensor.var.ty, tensor.value.ty)
+
+
 def test_lower_buffer_offset():
     @T.function(private=True)
     def before():

Reply via email to