This is an automated email from the ASF dual-hosted git repository.
tqchen pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new cd29ad03c1 [REFACTOR][TIRx] Consolidate bindings and emitted
allocation results (#20799)
cd29ad03c1 is described below
commit cd29ad03c1b6cac6852482c5fe2c8acf821ec395
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Oct 19 17:12:37 2026 -0400
[REFACTOR][TIRx] Consolidate bindings and emitted allocation results
(#20799)
---
python/tvm/topi/gpu/scan.py | 14 +-
python/tvm/topi/gpu/sort.py | 36 ++---
python/tvm/topi/searchsorted.py | 4 +-
python/tvm/topi/sparse_reshape.py | 16 ++-
python/tvm/topi/vision/nms.py | 18 +--
python/tvm/topi/vision/nms_util.py | 4 +-
.../transform/test_s_tir_transform_thread_sync.py | 16 +--
tirx/include/tvm/script/ir_builder/ir.h | 15 --
.../tvm_tirx/backend/cuda/lang/alloc_pool.py | 4 +-
tirx/python/tvm_tirx/backend/cuda/lang/pipeline.py | 15 +-
.../tvm_tirx/backend/cuda/lang/tile_scheduler.py | 28 ++--
.../backend/cuda/tile/patterns/async_copy.py | 4 +-
.../tvm_tirx/backend/cuda/tile/patterns/tma.py | 2 +-
tirx/python/tvm_tirx/ir/__init__.py | 1 -
tirx/python/tvm_tirx/ir/stmt.py | 18 ---
tirx/python/tvm_tirx/script/ir_builder/__init__.py | 2 +
tirx/python/tvm_tirx/script/ir_builder/base.py | 3 +-
tirx/python/tvm_tirx/script/ir_builder/stmt.py | 52 ++-----
tirx/python/tvm_tirx/testing/bench.py | 10 +-
tirx/python/tvm_tirx/tirx/script/__init__.py | 1 -
tirx/python/tvm_tirx/tirx/script/ir_builder/ir.py | 55 +++++---
.../tirx/script/ir_builder/parser_protocol.py | 91 ++++++------
tirx/src/script/ir_builder/ir.cc | 16 ---
.../tirx/analysis/test_check_well_defined.py | 24 ++--
tirx/tests/python/tirx/script/test_basic_usage.py | 15 +-
tirx/tests/python/tirx/script/test_ir_builder.py | 157 +++++++++++++++++++--
.../python/tirx/script/test_meta_programming.py | 6 +-
tirx/tests/python/tirx/script/test_printer.py | 39 ++---
.../python/tirx/script/test_printer_function.py | 4 +-
.../python/tirx/script/test_source_locations.py | 5 +-
tirx/tests/python/tirx/script/test_tensors.py | 10 +-
tirx/tests/python/tirx/script/test_tracing.py | 2 +-
tirx/tests/python/tirx/test_tile_namespace.py | 34 +++--
tirx/tests/python/tirx/tile/cuda/test_ewise.py | 2 +-
.../python/tirx/tile/cuda/test_tcgen05_mma.py | 2 +-
.../tirx/transform/test_common_subexpr_elim.py | 82 +++++------
.../python/tirx/transform/test_convert_ssa.py | 18 +--
.../python/tirx/transform/test_function_pass.py | 2 +-
.../tirx/transform/test_lower_tensor_layouts.py | 14 +-
39 files changed, 464 insertions(+), 377 deletions(-)
diff --git a/python/tvm/topi/gpu/scan.py b/python/tvm/topi/gpu/scan.py
index bd5bbc613b..f806e718e7 100644
--- a/python/tvm/topi/gpu/scan.py
+++ b/python/tvm/topi/gpu/scan.py
@@ -133,13 +133,13 @@ def exclusive_scan_ir(data, output, reduction=None,
binop=operator.add, identity
width = 2 << l2_width
blocks_per_batch = cast(ceil_div(scan_axis_size,
max_threads * width), "int32")
- start_buf = T.bind(
+ start_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")])
)
- middle_buf = T.bind(
+ middle_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")])
)
- end_buf = T.bind(
+ end_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")])
)
with T.frame_scope(
@@ -227,16 +227,16 @@ def exclusive_scan_ir(data, output, reduction=None,
binop=operator.add, identity
width = 2 << (lim - l2_width - 1)
blocks_per_batch = cast(ceil_div(scan_axis_size,
max_threads * width), "int32")
- start_buf = T.bind(
+ start_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")])
)
- middle_buf = T.bind(
+ middle_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")])
)
- end_buf = T.bind(
+ end_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")])
)
- tmp_buf = T.bind(
+ tmp_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], out_dtype,
scope="local")])
)
with T.frame_scope(
diff --git a/python/tvm/topi/gpu/sort.py b/python/tvm/topi/gpu/sort.py
index 4c3df93f16..6d3ab862f3 100644
--- a/python/tvm/topi/gpu/sort.py
+++ b/python/tvm/topi/gpu/sort.py
@@ -112,17 +112,21 @@ def _odd_even_sort(
start = bx * block_size
# Tensor declarations without data allocate their storage.
- tmp_keys_swap = T.bind(
+ tmp_keys_swap = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([block_size], keys_swap.dtype,
scope="shared")])
)
- temp_keys = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1],
keys_swap.dtype, scope="local")]))
- temp_cond1 = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1],
keys_swap.dtype, scope="local")]))
- temp_cond2 = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1],
keys_swap.dtype, scope="local")]))
+ temp_keys = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
keys_swap.dtype, scope="local")]))
+ temp_cond1 = T.bind_(
+ T.alloc_tensor(ty_args=[T.Tensor([1], keys_swap.dtype,
scope="local")])
+ )
+ temp_cond2 = T.bind_(
+ T.alloc_tensor(ty_args=[T.Tensor([1], keys_swap.dtype,
scope="local")])
+ )
if values_swap is not None:
- tmp_values_swap = T.bind(
+ tmp_values_swap = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([block_size],
values_swap.dtype, scope="shared")])
)
- temp_values = T.bind(
+ temp_values = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], values_swap.dtype,
scope="local")])
)
@@ -416,10 +420,10 @@ def _sort_common(
step_count,
even,
):
- first_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
- last_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
- i_buf_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
- j_buf_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
+ first_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
target_dtype, scope="local")]))
+ last_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
+ i_buf_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
target_dtype, scope="local")]))
+ j_buf_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
target_dtype, scope="local")]))
first = first_buf
last = last_buf
i_buf = i_buf_buf
@@ -483,16 +487,16 @@ def _sort_common(
step_count,
even,
):
- outer_first_buf = T.bind(
+ outer_first_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")])
)
- outer_last_buf = T.bind(
+ outer_last_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")])
)
- first_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
- last_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
- i_buf_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
- j_buf_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
+ first_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
target_dtype, scope="local")]))
+ last_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1], target_dtype,
scope="local")]))
+ i_buf_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
target_dtype, scope="local")]))
+ j_buf_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1],
target_dtype, scope="local")]))
outer_first = outer_first_buf
outer_last = outer_last_buf
first = first_buf
diff --git a/python/tvm/topi/searchsorted.py b/python/tvm/topi/searchsorted.py
index f0eda63934..39512653e9 100644
--- a/python/tvm/topi/searchsorted.py
+++ b/python/tvm/topi/searchsorted.py
@@ -38,8 +38,8 @@ def binary_search(sequence_offset, search_range,
sorted_sequence, value, right,
Note that we index N-D Buffer by 1-D linearlized indices.
"""
- lo_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], out_dtype,
scope="local")]))
- hi_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], out_dtype,
scope="local")]))
+ lo_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1], out_dtype,
scope="local")]))
+ hi_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1], out_dtype,
scope="local")]))
lo = lo_buf
hi = hi_buf
diff --git a/python/tvm/topi/sparse_reshape.py
b/python/tvm/topi/sparse_reshape.py
index 5542af272b..49a1d8c9dc 100644
--- a/python/tvm/topi/sparse_reshape.py
+++ b/python/tvm/topi/sparse_reshape.py
@@ -89,19 +89,19 @@ def sparse_reshape(
prev_shape_size = prev_shape_ptr.shape[0]
new_shape_size = new_shape_ptr.shape[0]
- multipliers_buf = T.bind(
+ multipliers_buf = T.bind_(
T.alloc_tensor(
ty_args=[T.Tensor([prev_shape_size], new_shape_ptr.dtype,
scope="local")]
)
)
multipliers = multipliers_buf
- dividers_buf = T.bind(
+ dividers_buf = T.bind_(
T.alloc_tensor(
ty_args=[T.Tensor([new_shape_size], new_shape_ptr.dtype,
scope="local")]
)
)
dividers = dividers_buf
- flattened_indices_buf = T.bind(
+ flattened_indices_buf = T.bind_(
T.alloc_tensor(
ty_args=[
T.Tensor([sparse_indices_ptr.shape[0]],
new_shape_ptr.dtype, scope="local")
@@ -109,15 +109,17 @@ def sparse_reshape(
)
)
flattened_indices = flattened_indices_buf
- total_ele_buf = T.bind(
+ total_ele_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], new_shape_ptr.dtype,
scope="local")])
)
total_ele = total_ele_buf
- division_total_ele_buf = T.bind(
+ division_total_ele_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor([1], new_shape_ptr.dtype,
scope="local")])
)
division_total_ele = division_total_ele_buf
- equal_shape_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1],
"bool", scope="local")]))
+ equal_shape_buf = T.bind_(
+ T.alloc_tensor(ty_args=[T.Tensor([1], "bool", scope="local")])
+ )
equal_shape = equal_shape_buf
T.tensor_store(
@@ -243,7 +245,7 @@ def sparse_reshape(
)
with T.parallel(0, new_sparse_indices_ptr.shape[0]) as i:
- current_element_buf = T.bind(
+ current_element_buf = T.bind_(
T.alloc_tensor(
ty_args=[T.Tensor([1], new_shape_ptr.dtype,
scope="local")]
)
diff --git a/python/tvm/topi/vision/nms.py b/python/tvm/topi/vision/nms.py
index 6d0dc136cc..d72d27413f 100644
--- a/python/tvm/topi/vision/nms.py
+++ b/python/tvm/topi/vision/nms.py
@@ -227,7 +227,7 @@ def _classic_nms_ir(
with T.parallel(0, batch_size) as i:
# Step 1: Reorder data by sorted score
- nkeep_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")]))
+ nkeep_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((1,),
"int32", scope="local")]))
nkeep_local = nkeep_buf
T.tensor_store(
nkeep_local,
@@ -266,20 +266,20 @@ def _classic_nms_ir(
T.tensor_store(out_box_indices, (i, j), T.int32(-1))
# Step 2: Apply NMS - greedy suppression
- num_valid_boxes_buf = T.bind(
+ num_valid_boxes_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")])
)
num_valid_boxes = num_valid_boxes_buf
T.tensor_store(num_valid_boxes, T.tensor_indices(num_valid_boxes,
0), T.int32(0))
- best_idx_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor((1,),
"int32", scope="local")]))
+ best_idx_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((1,),
"int32", scope="local")]))
best_idx = best_idx_buf
- best_score_buf = T.bind(
+ best_score_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor((1,), data.dtype,
scope="local")])
)
best_score = best_score_buf
- tmp_idx_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor((1,),
"int32", scope="local")]))
+ tmp_idx_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((1,),
"int32", scope="local")]))
tmp_idx = tmp_idx_buf
- tmp_val_buf = T.bind(
+ tmp_val_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor((1,), data.dtype,
scope="local")])
)
tmp_val = tmp_val_buf
@@ -635,7 +635,7 @@ def _classic_nms_ir(
)
if return_indices:
- valid_idx_buf = T.bind(
+ valid_idx_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")])
)
valid_idx = valid_idx_buf
@@ -883,7 +883,7 @@ def _rearrange_out(data, batch_size, num_anchors,
box_data_length, score_index):
out = outs[0]
with T.parallel(0, batch_size) as i:
- valid_idx_buf = T.bind(
+ valid_idx_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")])
)
valid_idx = valid_idx_buf
@@ -982,7 +982,7 @@ def _nms_loop(
with T.if_(tvm_tirx.tirx.all(iou_threshold > te.const(0),
valid_count[i] > te.const(0))):
with T.then_():
- num_valid_boxes_local_buf = T.bind(
+ num_valid_boxes_local_buf = T.bind_(
T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")])
)
num_valid_boxes_local = num_valid_boxes_local_buf
diff --git a/python/tvm/topi/vision/nms_util.py
b/python/tvm/topi/vision/nms_util.py
index 26fc0b32f0..322936286a 100644
--- a/python/tvm/topi/vision/nms_util.py
+++ b/python/tvm/topi/vision/nms_util.py
@@ -68,8 +68,8 @@ def binary_search(y, num_boxes, scores, score_threshold, out):
Must be called within an IRBuilder context.
"""
- lo_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")]))
- hi_buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")]))
+ lo_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")]))
+ hi_buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([1], "int32",
scope="local")]))
lo = lo_buf
hi = hi_buf
T.tensor_store(lo, T.tensor_indices(lo, 0), T.int32(0))
diff --git a/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
b/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
index 5a93835b94..a2e7a2e96b 100644
--- a/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
+++ b/tests/python/s_tir/transform/test_s_tir_transform_thread_sync.py
@@ -191,13 +191,13 @@ def test_sync_bind():
in_thread_A_temp.data, ty_args=[T.Tensor((1,), "float32",
scope="local")]
)
in_thread_A_temp_1[0] = T.float32(0)
- A_temp_1 = T.bind(in_thread_A_temp_1[0] + A_shared_1[threadIdx_x])
+ A_temp_1 = in_thread_A_temp_1[0] + A_shared_1[threadIdx_x]
in_thread_A_temp_1[0] = A_temp_1
- A_temp_2 = T.bind(in_thread_A_temp_1[0] + A_shared_1[threadIdx_x +
128])
+ A_temp_2 = in_thread_A_temp_1[0] + A_shared_1[threadIdx_x + 128]
in_thread_A_temp_1[0] = A_temp_2
- A_temp_3 = T.bind(in_thread_A_temp_1[0] + A_shared_1[threadIdx_x +
256])
+ A_temp_3 = in_thread_A_temp_1[0] + A_shared_1[threadIdx_x + 256]
in_thread_A_temp_1[0] = A_temp_3
- A_temp_4 = T.bind(in_thread_A_temp_1[0] + A_shared_1[threadIdx_x +
384])
+ A_temp_4 = in_thread_A_temp_1[0] + A_shared_1[threadIdx_x + 384]
in_thread_A_temp_1[0] = A_temp_4
cross_thread_A_temp_1 = T.decl_tensor(
cross_thread_A_temp.data, ty_args=[T.Tensor((1,), "float32",
scope="local")]
@@ -228,13 +228,13 @@ def test_sync_bind():
)
in_thread_A_temp_1_1[0] = T.float32(0)
T.evaluate(T.call_intrin("tirx.gpu_storage_sync", "shared",
ty="int32"))
- A_temp_1 = T.bind(in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x])
+ A_temp_1 = in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x]
in_thread_A_temp_1_1[0] = A_temp_1
- A_temp_2 = T.bind(in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x +
128])
+ A_temp_2 = in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x + 128]
in_thread_A_temp_1_1[0] = A_temp_2
- A_temp_3 = T.bind(in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x +
256])
+ A_temp_3 = in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x + 256]
in_thread_A_temp_1_1[0] = A_temp_3
- A_temp_4 = T.bind(in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x +
384])
+ A_temp_4 = in_thread_A_temp_1_1[0] + A_shared_1_1[threadIdx_x + 384]
in_thread_A_temp_1_1[0] = A_temp_4
cross_thread_A_temp_1_1 = T.decl_tensor(
cross_thread_A_temp_1.data, ty_args=[T.Tensor((1,), "float32",
scope="local")]
diff --git a/tirx/include/tvm/script/ir_builder/ir.h
b/tirx/include/tvm/script/ir_builder/ir.h
index 271914c8c3..dec738f722 100644
--- a/tirx/include/tvm/script/ir_builder/ir.h
+++ b/tirx/include/tvm/script/ir_builder/ir.h
@@ -95,21 +95,6 @@ ForFrame Grid(ffi::Array<ffi::Variant<PrimExpr,
ffi::Tuple<PrimExpr, PrimExpr>>>
AssertFrame Assert(PrimExpr condition, ffi::String error_kind,
ffi::Array<ffi::String> message_parts);
-/*!
- * \brief Create a Bind (variable binding).
- *
- * Emits a flat Bind statement to the current frame and returns the bound
variable.
- *
- * \param value The value to be bound.
- * \param type_annotation The type annotation of the binding.
- * Usually it is used for fine-grained var typing,
- * particularly, PtrType.
- * \param var The variable to be bound. If not specified, a new variable will
be created.
- * \return The bound Var.
- */
-Var Bind(Expr value, ffi::Optional<Type> type_annotation = std::nullopt,
- ffi::Optional<Var> var = std::nullopt);
-
/*!
* \brief Create a while loop.
* \param condition The termination condition of the loop.
diff --git a/tirx/python/tvm_tirx/backend/cuda/lang/alloc_pool.py
b/tirx/python/tvm_tirx/backend/cuda/lang/alloc_pool.py
index 3f790f6951..dd94dc9f75 100644
--- a/tirx/python/tvm_tirx/backend/cuda/lang/alloc_pool.py
+++ b/tirx/python/tvm_tirx/backend/cuda/lang/alloc_pool.py
@@ -185,9 +185,7 @@ class SMEMPool:
def __init__(self, ptr=_POOL_UNSET):
ir = _get_ir()
if ptr is _POOL_UNSET:
- self.buf = ir._bind_tensor(
- ir.alloc_tensor(ty_args=[ir.Tensor([0], "uint8",
scope="shared.dyn")])
- )
+ self.buf = ir.alloc_tensor(ty_args=[ir.Tensor([0], "uint8",
scope="shared.dyn")]).value
self.ptr = self.buf.data
self._owns_buffer = True
else:
diff --git a/tirx/python/tvm_tirx/backend/cuda/lang/pipeline.py
b/tirx/python/tvm_tirx/backend/cuda/lang/pipeline.py
index 460b5e1381..95242e1784 100644
--- a/tirx/python/tvm_tirx/backend/cuda/lang/pipeline.py
+++ b/tirx/python/tvm_tirx/backend/cuda/lang/pipeline.py
@@ -60,8 +60,8 @@ class PipelineState:
"""
def __init__(self, depth: int, phase=None):
- self.stage = T.local_scalar("int32")
- self.phase = T.local_scalar("int32")
+ self.stage = T.bind_(T.local_scalar("int32"))
+ self.phase = T.bind_(T.local_scalar("int32"))
self.depth = depth
if phase is not None:
self.init(phase)
@@ -93,7 +93,7 @@ def _map_addr_into_cta(ptr, rank):
the enclosing frame explicitly or it is discarded, and the scratch it
writes into has to be declared in this scope to bind into the Function.
"""
- mapped = T.alloc_local([1], "uint32")
+ mapped = T.bind_(T.alloc_local([1], "uint32"))
T.evaluate(
T.ptx.mapa.shared__cluster.u32(
mapped[0], T.cuda.cvta_generic_to_shared(ptr), T.uint32(rank)
@@ -113,11 +113,14 @@ def _map_buffer_into_cta(ptr, rank, depth):
from tvm_tirx.tirx import Var as TIRVar # pylint:
disable=import-outside-toplevel
ptr_ty = PtrType(PrimType("uint64"), "shared")
- mapped = T.alloc_local([1], "uint64")
+ mapped = T.bind_(T.alloc_local([1], "uint64"))
T.evaluate(T.ptx.mapa.u64(mapped[0], ptr, T.uint32(rank)))
remote_ptr = TIRVar("remote_mbar_ptr", ptr_ty)
- T.bind(T.reinterpret(mapped[0], ty=ptr_ty), var=remote_ptr)
- return T.bind(T.decl_tensor(remote_ptr, ty_args=[T.Tensor([depth],
"uint64", scope="shared")]))
+ T.emit_(T.Bind(remote_ptr, T.reinterpret(mapped[0], ty=ptr_ty)))
+ value = T.decl_tensor(remote_ptr, ty_args=[T.Tensor([depth], "uint64",
scope="shared")])
+ remote_barrier = TIRVar("remote_mbar", value.ty)
+ T.emit_(T.Bind(remote_barrier, value))
+ return remote_barrier
def _mbarrier_arrive_remote(bar, pred=None, count=None):
diff --git a/tirx/python/tvm_tirx/backend/cuda/lang/tile_scheduler.py
b/tirx/python/tvm_tirx/backend/cuda/lang/tile_scheduler.py
index 24eba47b80..a99b6c41e7 100644
--- a/tirx/python/tvm_tirx/backend/cuda/lang/tile_scheduler.py
+++ b/tirx/python/tvm_tirx/backend/cuda/lang/tile_scheduler.py
@@ -30,9 +30,9 @@ class BaseTileScheduler:
"""Base class for tile schedulers with common state and macros."""
def __init__(self, prefix: str):
- self.m_idx = T.local_scalar("int32")
- self.n_idx = T.local_scalar("int32")
- self.linear_idx = T.local_scalar("int32")
+ self.m_idx = T.bind_(T.local_scalar("int32"))
+ self.n_idx = T.bind_(T.local_scalar("int32"))
+ self.linear_idx = T.bind_(T.local_scalar("int32"))
@T.inline
def update_current_m_n_idx(self, linear_idx):
@@ -231,7 +231,7 @@ class ClusterPersistentScheduler2D(BaseTileScheduler):
# Rename internal state for clarity
self.work_idx = self.linear_idx # alias: global work item index
- self.tile_count = T.local_scalar("int32")
+ self.tile_count = T.bind_(T.local_scalar("int32"))
is_static_m = isinstance(num_m_tiles, int)
@@ -509,9 +509,9 @@ class FlashAttentionLinearScheduler(BaseTileScheduler):
self._total_tasks = num_batches * num_heads * num_m_blocks
# Output indices
- self.batch_idx = T.local_scalar("int32")
- self.head_idx = T.local_scalar("int32")
- self.m_block_idx = T.local_scalar("int32")
+ self.batch_idx = T.bind_(T.local_scalar("int32"))
+ self.head_idx = T.bind_(T.local_scalar("int32"))
+ self.m_block_idx = T.bind_(T.local_scalar("int32"))
# fmt: off
@T.inline
@@ -596,9 +596,9 @@ class FlashAttentionLPTScheduler(BaseTileScheduler):
self._num_hb_quotient = self._num_hb // l2_swizzle
# Output indices
- self.batch_idx = T.local_scalar("int32")
- self.head_idx = T.local_scalar("int32")
- self.m_block_idx = T.local_scalar("int32")
+ self.batch_idx = T.bind_(T.local_scalar("int32"))
+ self.head_idx = T.bind_(T.local_scalar("int32"))
+ self.m_block_idx = T.bind_(T.local_scalar("int32"))
# fmt: off
@T.inline
@@ -670,8 +670,8 @@ class _CLCWorker(ClusterPersistentScheduler2D):
)
self._clc = clc
self._sa = PipelineState(1, 0)
- self._done = T.local_scalar("int32")
- self._nxt = T.local_scalar("uint32")
+ self._done = T.bind_(T.local_scalar("int32"))
+ self._nxt = T.bind_(T.local_scalar("uint32"))
@T.inline
def reset(self):
@@ -735,8 +735,8 @@ class ClusterLaunchControlScheduler:
self.sched_arr = Pipeline(pool, 1, full="tma", empty="mbar",
init_empty=1)
self.sched_fin = Pipeline(pool, 1, full="mbar", empty="mbar",
init_empty=finish_arrivals)
self.clc_handle = pool.alloc((4,), "uint32", align=16)
- self._s_done = T.local_scalar("int32")
- self._s_nxt = T.local_scalar("uint32")
+ self._s_done = T.bind_(T.local_scalar("int32"))
+ self._s_nxt = T.bind_(T.local_scalar("uint32"))
def worker(self, prefix):
return _CLCWorker(self, prefix)
diff --git a/tirx/python/tvm_tirx/backend/cuda/tile/patterns/async_copy.py
b/tirx/python/tvm_tirx/backend/cuda/tile/patterns/async_copy.py
index 4ae853aed5..3644da6565 100644
--- a/tirx/python/tvm_tirx/backend/cuda/tile/patterns/async_copy.py
+++ b/tirx/python/tvm_tirx/backend/cuda/tile/patterns/async_copy.py
@@ -31,7 +31,7 @@ before mapa. Layout strides are elements; chunk_bytes and
src_size are bytes.
import tvm_ffi
from tvm_tirx.backend.cuda.ptx import PTXNamespace
-from tvm_tirx.ir import Bind, Evaluate, Var
+from tvm_tirx.ir import Bind, Call, Evaluate, Var
from tvm_tirx.script import tirx as T
from .common import Context, scalar
@@ -66,7 +66,7 @@ def lower(opcode, entry, modifiers, args, params, sctx):
if bulk:
barrier, remote = params.pop("mbar"), params.pop("remote_cta_id")
mapped = Var("remote_addresses", T.Tensor((2,), "uint64",
scope="local", layout="default"))
- ctx.bindings.append(Bind(mapped, T.alloc_tensor(ty_args=[mapped.ty])))
+ ctx.bindings.append(Bind(mapped, Call("tirx.alloc_tensor", [],
ty_args=[mapped.ty])))
ctx.bindings.append(
Evaluate(T.ptx.mapa.u64(mapped[0], barrier, T.cast(remote,
ty="uint32")))
)
diff --git a/tirx/python/tvm_tirx/backend/cuda/tile/patterns/tma.py
b/tirx/python/tvm_tirx/backend/cuda/tile/patterns/tma.py
index 272dd1209e..a85eeec50b 100644
--- a/tirx/python/tvm_tirx/backend/cuda/tile/patterns/tma.py
+++ b/tirx/python/tvm_tirx/backend/cuda/tile/patterns/tma.py
@@ -202,7 +202,7 @@ def _tensor_map(ctx, region, rank, box, encoding):
@T.function(check_well_formed=False)
def encode():
- T.bind(T.stack_alloca("tensormap", 1), var=tensor_map)
+ T.Bind(tensor_map, T.stack_alloca("tensormap", 1))
T.cuda.tensormap_encode_tiled(
tensor_map,
tensor.ptr_to(starts),
diff --git a/tirx/python/tvm_tirx/ir/__init__.py
b/tirx/python/tvm_tirx/ir/__init__.py
index d10809eab8..8a4d3ea4dc 100644
--- a/tirx/python/tvm_tirx/ir/__init__.py
+++ b/tirx/python/tvm_tirx/ir/__init__.py
@@ -85,7 +85,6 @@ from .stmt import (
RegionStmt,
TensorStore,
stmt_seq,
- stmt_list,
)
# Load passes after the common IR classes used by transformations are
registered.
diff --git a/tirx/python/tvm_tirx/ir/stmt.py b/tirx/python/tvm_tirx/ir/stmt.py
index b7bdddd877..d3cd1cba81 100644
--- a/tirx/python/tvm_tirx/ir/stmt.py
+++ b/tirx/python/tvm_tirx/ir/stmt.py
@@ -472,21 +472,3 @@ def stmt_seq(*args: Expr | Stmt) -> SeqStmt:
value = Evaluate(value)
ret.append(value)
return SeqStmt(ret)
-
-
-def stmt_list(stmt: Stmt) -> list[Stmt]:
- """Make list of stmt from blocks.
-
- Parameters
- ----------
- stmt : Stmt
- The input statement.
-
- Returns
- -------
- stmt_list : List[Stmt]
- The unpacked list of statements
- """
- if isinstance(stmt, SeqStmt):
- return list(stmt.seq)
- return [stmt]
diff --git a/tirx/python/tvm_tirx/script/ir_builder/__init__.py
b/tirx/python/tvm_tirx/script/ir_builder/__init__.py
index bf2b887ab5..68b9c2e1f8 100644
--- a/tirx/python/tvm_tirx/script/ir_builder/__init__.py
+++ b/tirx/python/tvm_tirx/script/ir_builder/__init__.py
@@ -17,6 +17,7 @@
"""Shared TVMScript construction APIs and lazy language variant builders."""
from tvm_tirx.ir import (
+ Bind,
Call,
DataTypeImm,
FuncType,
@@ -68,6 +69,7 @@ from .parser_protocol import (
__all__ = [
"MISSING",
"AlreadyEmitted",
+ "Bind",
"Call",
"DataTypeImm",
"FuncType",
diff --git a/tirx/python/tvm_tirx/script/ir_builder/base.py
b/tirx/python/tvm_tirx/script/ir_builder/base.py
index 4b9ee703d1..65f6ebe7da 100644
--- a/tirx/python/tvm_tirx/script/ir_builder/base.py
+++ b/tirx/python/tvm_tirx/script/ir_builder/base.py
@@ -297,8 +297,9 @@ class AlreadyEmitted(Generic[_T]):
Parameters
----------
value : T
- Native result retained without copying or emitting it. Operator APIs
+ Native result retained without copying or emitting it. Void operator
APIs
wrap their original Call; statement APIs wrap the emitted statement.
+ Allocation APIs wrap the bound Var or its scalar TensorLoad view.
Attributes
----------
diff --git a/tirx/python/tvm_tirx/script/ir_builder/stmt.py
b/tirx/python/tvm_tirx/script/ir_builder/stmt.py
index 402956631f..e8bba6641e 100644
--- a/tirx/python/tvm_tirx/script/ir_builder/stmt.py
+++ b/tirx/python/tvm_tirx/script/ir_builder/stmt.py
@@ -30,7 +30,7 @@ from typing import Any
import tvm_tirx
from tvm_tirx import ir as _ir
-from tvm_tirx.ir import Expr, IntImm, TensorRegion, Type, Var
+from tvm_tirx.ir import Expr, IntImm, TensorRegion, Var
from tvm_tirx.ir import StringImm as _StringImm
from tvm_tirx.script.ir_builder import base as _base
from tvm_tirx.script.ir_builder.base import AlreadyEmitted
@@ -53,8 +53,19 @@ def _as_expr(value):
def _enter_concise(frame: _base.IRBuilderFrame) -> Any:
- # add_callback registers on the active parent before the child enters.
- # Later statements emit into the child; parent exit closes this scope.
+ """Enter a child scope that lasts until the active parent exits.
+
+ Register the child's exit callback on the parent before entering the child.
+ Subsequent statements are emitted into the child; exiting the parent first
+ closes the child and incorporates its statements into the parent.
+
+ For example, inside an active IRBuilder::
+
+ with T.function():
+ T.func_name_("example")
+ i = _enter_concise(T.serial(4))
+ T.evaluate(i) # Emitted inside the loop, closed on function exit.
+ """
frame.add_callback(_partial(frame.__exit__, None, None, None))
return frame.__enter__()
@@ -68,41 +79,6 @@ def unpack_(value: Any) -> Any:
return value
-def bind( # pylint: disable=invalid-name
- value: Expr,
- type_annotation: Type | None = None, # pylint:
disable=redefined-outer-name
- *,
- var: Var | None = None, # pylint: disable=redefined-outer-name
-) -> Var:
- """Create a Bind (variable binding).
-
- Emits a flat Bind statement to the current frame and returns the bound
variable.
-
- Parameters
- ----------
- value : Expr
- The value to be bound.
- type_annotation : Optional[Type] = None
- The type annotation of the binding. Usually it is used for
fine-grained var typing,
- particularly, PtrType.
- var : Optional[Var] = None
- The variable to bind. If not specified, a new variable will be created.
-
- Returns
- -------
- var : Var
- The bound variable.
- """
- if type_annotation is not None:
- # Canonical Vars are callable when they denote functions. Here a Var
is
- # already a resolved type annotation, rather than a deferred
annotation factory.
- if callable(type_annotation) and not isinstance(type_annotation, Expr
| _ir.Type):
- type_annotation = type_annotation()
- if isinstance(type_annotation, _ir.Var):
- type_annotation = type_annotation.ty
- return _ffi_api.Bind(value, type_annotation, var) # type:
ignore[attr-defined] # pylint: disable=no-member
-
-
def evaluate(
value: Expr | AlreadyEmitted[tvm_tirx.ir.Call] |
AlreadyEmitted[tvm_tirx.ir.Stmt],
) -> AlreadyEmitted[tvm_tirx.ir.Call] | AlreadyEmitted[tvm_tirx.ir.Stmt]:
diff --git a/tirx/python/tvm_tirx/testing/bench.py
b/tirx/python/tvm_tirx/testing/bench.py
index 4e0cafa25c..68dd8c6786 100644
--- a/tirx/python/tvm_tirx/testing/bench.py
+++ b/tirx/python/tvm_tirx/testing/bench.py
@@ -940,11 +940,13 @@ class CudaProfiler:
# Assume Expr-like input; use as-is
self.profiler_enabled = profiler_enabled # type:
ignore[assignment]
- self.profiler_tag = T.bind(
- T.alloc_tensor(ty_args=[T.Tensor([1], "uint64", scope="local",
align=8)])
+ self.profiler_tag = T.bind_(
+ T.alloc_tensor(ty_args=[T.Tensor([1], "uint64", scope="local",
align=8)]),
+ name="profiler_tag",
)
- self.profiler_write_offset = T.bind(
- T.alloc_tensor(ty_args=[T.Tensor([1], "uint32", scope="local",
align=8)])
+ self.profiler_write_offset = T.bind_(
+ T.alloc_tensor(ty_args=[T.Tensor([1], "uint32", scope="local",
align=8)]),
+ name="profiler_write_offset",
)
def _leader(self, leader: None | tvm_tirx.tirx.Expr | bool):
diff --git a/tirx/python/tvm_tirx/tirx/script/__init__.py
b/tirx/python/tvm_tirx/tirx/script/__init__.py
index e56b713dcd..88d0ef8ae0 100644
--- a/tirx/python/tvm_tirx/tirx/script/__init__.py
+++ b/tirx/python/tvm_tirx/tirx/script/__init__.py
@@ -41,7 +41,6 @@ def _initialize() -> None:
(name, value) for name, value in vars(builder).items() if not
name.startswith("_")
)
globals().update(
- bind=builder.bind,
ir_module=entry.ir_module,
pyfunc=entry.pyfunc,
function=entry.make_decorator(builder,
namespace_path="tirx.function"),
diff --git a/tirx/python/tvm_tirx/tirx/script/ir_builder/ir.py
b/tirx/python/tvm_tirx/tirx/script/ir_builder/ir.py
index a13cfd9b2f..b2784a083a 100644
--- a/tirx/python/tvm_tirx/tirx/script/ir_builder/ir.py
+++ b/tirx/python/tvm_tirx/tirx/script/ir_builder/ir.py
@@ -194,6 +194,7 @@ def elected():
)
+@_register_mutable_decl("tirx.alloc_tensor")
def alloc_tensor(
placement=None,
*,
@@ -202,12 +203,16 @@ def alloc_tensor(
annotations: dict[str, Any] | None = None,
loc: Location = UNKNOWN_LOC,
ty=None,
-) -> ir.Call:
+) -> ir.Call | AlreadyEmitted[ir.Var]:
"""Allocate the tensor described by one explicit ``Tensor`` type argument.
Optional placement is an ordinary tuple operand. Allocation annotations
belong to ``attrs``; ``annotations`` accepts a Python dictionary whose
- scalar values are normalized to typed IR constants.
+ scalar values are normalized to typed IR constants. Under an active
builder,
+ emit one binding and return its tensor variable in an ``AlreadyEmitted``
+ receipt. Outside a builder return
+ the allocation Call. Construct ``ir.Call("tirx.alloc_tensor", ...)``
directly
+ when a pure expression is needed inside a builder.
"""
if attrs is not None and annotations is not None:
raise TypeError("alloc_tensor cannot mix attrs and annotations")
@@ -236,7 +241,7 @@ def alloc_tensor(
)
placement = ir.Tuple([normalize(value) for value in placement])
args.append(placement)
- return ir.Call(
+ value = ir.Call(
"tirx.alloc_tensor",
args,
attrs=attrs,
@@ -245,11 +250,17 @@ def alloc_tensor(
ty=ty,
)
+ return AlreadyEmitted(_bind_tensor(value)) if IRBuilder.is_in_scope() else
value
+
def _bind_tensor(value):
- from tvm_tirx.script.ir_builder.stmt import bind # pylint:
disable=import-outside-toplevel
+ from tvm_tirx.script.ir_builder.stmt import (
+ add_to_parent, # pylint: disable=import-outside-toplevel
+ )
- tensor = bind(value)
+ IRBuilder.current()._attach_loc(value)
+ tensor = ir.Var("v", value.ty)
+ add_to_parent(ir.Bind(tensor, value))
_record_meta_resource(tensor, skip_frames=2)
return tensor
@@ -263,8 +274,9 @@ def _alloc_tensor(
layout=MISSING,
allocated_addr=None,
annotations=None,
-):
- """Implement separately named allocation conveniences using the canonical
Op."""
+) -> AlreadyEmitted[ir.Var]:
+ """Emit a tensor allocation and return its bound variable in a receipt."""
+ IRBuilder.current()
scope = MemoryScope(scope)
if allocated_addr is not None:
if scope in (_SCOPE_GLOBAL, _SCOPE_SHARED, _SCOPE_SHARED_DYN,
_SCOPE_LOCAL):
@@ -281,9 +293,7 @@ def _alloc_tensor(
layout,
data=data,
)
- return _bind_tensor(
- alloc_tensor(allocated_addr, ty_args=[tensor_type],
annotations=annotations)
- )
+ return alloc_tensor(allocated_addr, ty_args=[tensor_type],
annotations=annotations)
class LetAnnotation:
@@ -363,16 +373,18 @@ def alloc_scalar(
scope: str | MemoryScope = _SCOPE_GLOBAL,
*,
annotations: dict[str, Any] | None = None,
-) -> _ir.TensorLoad:
+) -> AlreadyEmitted[_ir.TensorLoad]:
"""Allocate a zero-dimensional tensor (scalar), with optional allocation
annotations."""
- buf = _alloc_tensor(shape=(1,), dtype=dtype, scope=scope, layout=None,
annotations=annotations)
+ buf = _alloc_tensor(
+ shape=(1,), dtype=dtype, scope=scope, layout=None,
annotations=annotations
+ ).value
assert is_tensor_var(buf)
scalar = buf[0]
- return scalar
+ return AlreadyEmitted(scalar)
@_register_mutable_decl("tirx.decl_scalar")
-def decl_scalar(dtype, data, scope, layout=None) -> _ir.TensorLoad:
+def decl_scalar(dtype, data, scope, layout=None) ->
AlreadyEmitted[_ir.TensorLoad]:
"""Declare a zero-dimensional tensor (scalar) from a pointer."""
buf = _bind_tensor(
decl_tensor(
@@ -390,13 +402,13 @@ def decl_scalar(dtype, data, scope, layout=None) ->
_ir.TensorLoad:
)
assert is_tensor_var(buf)
scalar = buf[0]
- return scalar
+ return AlreadyEmitted(scalar)
@_register_mutable_decl("tirx.shared_scalar")
def shared_scalar(
dtype: str = "float32", *, annotations: dict[str, Any] | None = None
-) -> _ir.TensorLoad:
+) -> AlreadyEmitted[_ir.TensorLoad]:
"""Allocate a zero-dimensional tensor in shared memory."""
return alloc_scalar(dtype=dtype, scope="shared", annotations=annotations)
@@ -408,7 +420,7 @@ def local_scalar(
*,
name: str | None = None,
annotations: dict[str, Any] | None = None,
-) -> _ir.TensorLoad:
+) -> AlreadyEmitted[_ir.TensorLoad]:
"""Allocate scalar local storage, optionally naming and initializing it.
Parameters
@@ -425,15 +437,16 @@ def local_scalar(
Returns
-------
- scalar : TensorLoad
- A load from index zero of the one-element local tensor, with no layout.
+ scalar : AlreadyEmitted[TensorLoad]
+ A receipt for the load from index zero of the one-element local tensor.
"""
- scalar = alloc_scalar(dtype=dtype, scope="local", annotations=annotations)
+ result = alloc_scalar(dtype=dtype, scope="local", annotations=annotations)
+ scalar = result.value
if name is not None:
IRBuilder.name(name, scalar.source)
if init is not None:
assign(scalar, init)
- return scalar
+ return result
def assign(dst: _ir.TensorLoad | _ir.Var, value: Any) ->
AlreadyEmitted[_ir.Stmt]:
diff --git a/tirx/python/tvm_tirx/tirx/script/ir_builder/parser_protocol.py
b/tirx/python/tvm_tirx/tirx/script/ir_builder/parser_protocol.py
index 42387cc3a2..87e14747a8 100644
--- a/tirx/python/tvm_tirx/tirx/script/ir_builder/parser_protocol.py
+++ b/tirx/python/tvm_tirx/tirx/script/ir_builder/parser_protocol.py
@@ -31,9 +31,6 @@ from typing import Any
import tvm_ffi
import tvm_tirx
-
-# isort: off
-# isort: on
from tvm_tirx import ir as _ir
from tvm_tirx import tirx as _tir
from tvm_tirx.ir import IntImm, is_prim_expr
@@ -43,51 +40,26 @@ from tvm_tirx.script.ir_builder import base as _base
from tvm_tirx.script.ir_builder import frame as _shared_frame
from tvm_tirx.script.ir_builder.base import AlreadyEmitted
from tvm_tirx.script.ir_builder.base import IRBuilder as _IRBuilder
+
+# isort: off
from tvm_tirx.script.ir_builder.stmt import (
_enter_concise as _enter_concise,
-)
-from tvm_tirx.script.ir_builder.stmt import (
add_to_parent as add_to_parent,
-)
-from tvm_tirx.script.ir_builder.stmt import (
assert_ as assert_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
- bind as _bind,
-)
-from tvm_tirx.script.ir_builder.stmt import (
break_ as break_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
continue_ as continue_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
else_ as else_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
evaluate as evaluate,
-)
-from tvm_tirx.script.ir_builder.stmt import (
grid as grid,
-)
-from tvm_tirx.script.ir_builder.stmt import (
if_ as if_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
region as region,
-)
-from tvm_tirx.script.ir_builder.stmt import (
return_ as return_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
then_ as then_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
unpack_ as unpack_,
-)
-from tvm_tirx.script.ir_builder.stmt import (
while_ as while_,
)
+
+# isort: on
from tvm_tirx.tirx import Expr
from . import _ffi_api, frame
@@ -226,14 +198,6 @@ def _name(value: Any, name: str | None, loc: _Loc) -> Any:
return _base.at_(loc, value)
-def bind(value, type_annotation=None, *, var=None):
- """Bind an expression and track tensor resources created in meta
classes."""
- result = _bind(value, type_annotation, var=var)
- if _tir.is_tensor_var(result):
- _native._record_meta_resource(result)
- return result
-
-
def bind_(
value: Any = _base.MISSING,
*,
@@ -248,9 +212,26 @@ def bind_(
Returned Vars, including tensors, and metadata retain identity, names and
locs.
Other expressions create native Bind nodes; value_loc belongs to the RHS.
- Explicit typed bindings and frame targets retain their separate contracts.
+ Emitted allocation receipts unwrap to their original Var or TensorLoad,
+ checking annotations without creating another binding. Other receipts
remain
+ intact. Explicit typed bindings and frame targets retain their separate
contracts.
"""
name_loc = loc if name_loc is None else name_loc
+ if isinstance(value, _base.AlreadyEmitted):
+ result = _base.at_(value_loc, value).value
+ if not isinstance(result, _ir.Var | _ir.TensorLoad):
+ return value
+ if ty is not None:
+ if isinstance(ty, _native.LetAnnotation):
+ annotation = ty.as_var(rhs_dtype=result.ty).ty
+ else:
+ annotation = (
+ ty() if callable(ty) and not isinstance(ty, _ir.Expr |
_ir.Type) else ty
+ )
+ annotation = annotation.ty if isinstance(annotation, _ir.Expr)
else annotation
+ _ir.assert_structural_equal(result.ty, annotation)
+ _name(result.source if isinstance(result, _ir.TensorLoad) else result,
name, name_loc)
+ return result
if frame_value:
if isinstance(value, _python.list | _python.tuple | tvm_ffi.Array):
for index, item in enumerate(value):
@@ -273,7 +254,7 @@ def bind_(
if not isinstance(value, _ir.Var):
_base.at_(value_loc, value)
variable = _name(ty.as_var(rhs_dtype=value.ty), name, name_loc)
- _base.with_at_group_(loc, lambda: bind(value, var=variable))
+ _base.with_at_group_(loc, lambda: emit_(_ir.Bind(variable, value)))
return variable
if ty is not None:
annotation = ty() if callable(ty) and not isinstance(ty, _ir.Expr |
_ir.Type) else ty
@@ -282,11 +263,10 @@ def bind_(
if not isinstance(value, _ir.Var):
_base.at_(value_loc, value)
variable = _ir.Var(name or "", annotation)
- return _name(_base.with_at_group_(loc, lambda: bind(value,
var=variable)), name, name_loc)
+ _base.with_at_group_(loc, lambda: emit_(_ir.Bind(variable, value)))
+ return _name(variable, name, name_loc)
if value is _base.MISSING:
raise ValueError("An uninitialized binding requires a scalar type
annotation")
- if isinstance(value, _base.AlreadyEmitted):
- return _base.at_(value_loc, value)
if isinstance(value, _base.IRBuilderFrame):
frame_loc = value_loc if value_loc is not None else loc
return _name(_enter_concise(_base.at_(frame_loc, value)), name,
name_loc)
@@ -299,7 +279,9 @@ def bind_(
if not isinstance(value, _ir.Expr | _python.int | _python.float |
_python.bool | str):
return value
value = _base.at_(value_loc, _op._as_expr(value))
- return _name(_base.with_at_group_(loc, lambda: bind(value)), name,
name_loc)
+ variable = _ir.Var("v", value.ty)
+ _base.with_at_group_(loc, lambda: emit_(_ir.Bind(variable, value)))
+ return _name(variable, name, name_loc)
def decl_mutable_cell_(
@@ -316,11 +298,21 @@ def decl_mutable_cell_(
allocate their declared shape. Var declaration producers retain their own
effects.
"""
name_loc = loc if name_loc is None else name_loc
+ if isinstance(value, _base.AlreadyEmitted):
+ return bind_(value, ty=ty, name=name, loc=loc, name_loc=name_loc)
+ if _tir.is_tensor_var(value) and ty is not None:
+ if isinstance(ty, _native.LetAnnotation):
+ annotation = ty.as_var(rhs_dtype=value.ty).ty
+ else:
+ annotation = ty() if callable(ty) and not isinstance(ty, _ir.Expr
| _ir.Type) else ty
+ annotation = annotation.ty if isinstance(annotation, _ir.Expr)
else annotation
+ _ir.assert_structural_equal(value.ty, annotation)
+ return _name(value, name, name_loc)
if isinstance(ty, _PrimTypeArrayAnnotation):
if value is not _base.MISSING:
raise ValueError("Vector annotation does not support an
initializer")
return _name(
- _base.with_at_group_(loc, lambda: _native.alloc_local(ty.shape,
ty.dtype)),
+ _base.with_at_group_(loc, lambda: _native.alloc_local(ty.shape,
ty.dtype)).value,
name,
name_loc,
)
@@ -329,7 +321,7 @@ def decl_mutable_cell_(
annotation = annotation.ty if isinstance(annotation, _ir.Expr) else
annotation
if not isinstance(annotation, _ir.PrimType) or str(annotation) ==
"handle":
raise TypeError("Mutable scalar annotations require a primitive
scalar type")
- storage = _base.with_at_group_(loc, lambda:
_native.local_scalar(str(annotation)))
+ storage = _base.with_at_group_(loc, lambda:
_native.local_scalar(str(annotation))).value
if value is not _base.MISSING:
set_mutable_cell_(storage, value, loc=loc)
else:
@@ -391,6 +383,8 @@ def emit_(value: Any, *, loc: _Loc = None) -> None:
_enter_concise(_base.at_(loc, frame))
elif isinstance(value, tvm_tirx.ir.Stmt):
add_to_parent(_base.at_(loc, value))
+ if isinstance(value, _ir.Bind) and _tir.is_tensor_var(value.var):
+ _native._record_meta_resource(value.var)
else:
# Native conversion owns Python literals; annotate the exact expression
# it stored, as well as the statement, without converting or emitting
twice.
@@ -788,7 +782,6 @@ __all__ = [
"and_",
"arg_",
"assert_",
- "bind",
"bind_",
"break_",
"call_global_var_",
diff --git a/tirx/src/script/ir_builder/ir.cc b/tirx/src/script/ir_builder/ir.cc
index 9bb4e99059..c855dd9f4a 100644
--- a/tirx/src/script/ir_builder/ir.cc
+++ b/tirx/src/script/ir_builder/ir.cc
@@ -290,21 +290,6 @@ AssertFrame Assert(PrimExpr condition, ffi::String
error_kind,
return AssertFrame(n);
}
-Var Bind(Expr value, ffi::Optional<Type> type_annotation, ffi::Optional<Var>
var) {
- Expr value_expr = value;
- Var bind_var = [&]() {
- if (var.has_value()) {
- return var.value();
- } else if (type_annotation.has_value()) {
- return Var("v", type_annotation.value());
- } else {
- return Var("v", value_expr->ty);
- }
- }();
- AddToParent(tvm::Bind(bind_var, value_expr));
- return bind_var;
-}
-
RegionFrame Region(Op op, ffi::Array<Expr> args,
ffi::Optional<ffi::Array<Var>> body_params,
DictAttrs attrs) {
TVM_FFI_CHECK(tvm::IsRegionOp(op), ValueError)
@@ -385,7 +370,6 @@ TVM_FFI_STATIC_INIT_BLOCK() {
.def("script.ir_builder.For", For)
.def("script.ir_builder.Grid", Grid)
.def("script.ir_builder.Assert", Assert)
- .def("script.ir_builder.Bind", Bind)
.def("script.ir_builder.While", While)
.def("script.ir_builder.Return", Return)
.def("script.ir_builder.Break", Break)
diff --git a/tirx/tests/python/tirx/analysis/test_check_well_defined.py
b/tirx/tests/python/tirx/analysis/test_check_well_defined.py
index c781d95e04..f5806fbc27 100644
--- a/tirx/tests/python/tirx/analysis/test_check_well_defined.py
+++ b/tirx/tests/python/tirx/analysis/test_check_well_defined.py
@@ -80,8 +80,8 @@ def test_error_for_nested_rebind_usage():
@T.function(check_well_formed=False)
def func():
- T.bind(42, var=i)
- T.bind(42, var=i)
+ T.Bind(i, 42)
+ T.Bind(i, 42)
T.evaluate(i)
with pytest.raises(
@@ -103,9 +103,9 @@ def test_error_for_repeated_binding():
@T.function(check_well_formed=False)
def func():
- T.bind(42, var=i)
+ T.Bind(i, 42)
T.evaluate(i)
- T.bind(17, var=i)
+ T.Bind(i, 17)
T.evaluate(i)
with pytest.raises(
@@ -124,12 +124,12 @@ def test_error_for_cross_function_reuse():
class mod:
@T.function
def func1():
- T.bind(42, var=i)
+ T.Bind(i, 42)
T.evaluate(i)
@T.function
def func2():
- T.bind(42, var=i)
+ T.Bind(i, 42)
T.evaluate(i)
with pytest.raises((ValueError, RuntimeError), match="multiple definitions
of variable i"):
@@ -215,10 +215,10 @@ def
test_error_message_without_previous_definition_location():
@T.function(check_well_formed=False)
def func():
- T.bind(42, var=x)
+ T.Bind(x, 42)
T.evaluate(x)
- T.bind(99, var=x) # This should trigger the error
+ T.Bind(x, 99) # This should trigger the error
T.evaluate(x)
with pytest.raises((ValueError, RuntimeError)) as exc_info:
@@ -242,8 +242,8 @@ def test_error_message_with_previous_definition_location():
@T.function(check_well_formed=False)
def func():
- T.bind(42, var=x)
- T.bind(99, var=x) # This should trigger the error
+ T.Bind(x, 42)
+ T.Bind(x, 99) # This should trigger the error
T.evaluate(x)
with pytest.raises((ValueError, RuntimeError)) as exc_info:
@@ -271,10 +271,10 @@ def test_sequential_redefinition_with_location():
@T.function(check_well_formed=False)
def func():
- T.bind(1, var=x)
+ T.Bind(x, 1)
T.evaluate(x)
- T.bind(2, var=x) # This should trigger the error
+ T.Bind(x, 2) # This should trigger the error
T.evaluate(x)
with pytest.raises((ValueError, RuntimeError)) as exc_info:
diff --git a/tirx/tests/python/tirx/script/test_basic_usage.py
b/tirx/tests/python/tirx/script/test_basic_usage.py
index 67199b35f9..9834831fab 100644
--- a/tirx/tests/python/tirx/script/test_basic_usage.py
+++ b/tirx/tests/python/tirx/script/test_basic_usage.py
@@ -1340,8 +1340,8 @@ def elif_chain_with_else():
def bind_var():
@T.function
def func():
- x = T.bind(0)
- y = T.bind(0)
+ x = 0
+ y = 0
T.evaluate(0)
T.evaluate(0)
@@ -1599,18 +1599,17 @@ def test_implicit_evaluate_call_extern():
def test_preserve_trivial_let_binding():
- """Trivial `T.let[...]` annotations survive the parser as LetStmt and are
not inlined.
+ """Trivial `T.let[...]` annotations survive the parser as Bind and are not
inlined.
- In fork, bare `j = i` lowers to a local_scalar (AllocTensor +
TensorStore); the
- LetStmt form is opt-in via `T.let[T.dtype]`. Both the explicit
`T.bind(..., var=j)`
- builder API and the `j: T.let[T.dtype]` annotation produce the same
LetStmt IR.
+ Both an explicit `T.Bind(j, i)` statement and the `j: T.let[T.dtype]`
+ annotation produce the same Bind IR, preserving a distinct variable for
`j`.
"""
j = T.dynamic("j", "int32")
@T.function
def explicit(i: T.int32):
- T.bind(i, var=j)
+ T.Bind(j, i)
T.evaluate(j)
@T.function
@@ -1628,7 +1627,7 @@ def test_preserve_trivial_let_binding_of_value():
@T.function
def explicit(i: T.int32):
- T.bind(42, var=j)
+ T.Bind(j, 42)
T.evaluate(j)
@T.function
diff --git a/tirx/tests/python/tirx/script/test_ir_builder.py
b/tirx/tests/python/tirx/script/test_ir_builder.py
index 3b414f091e..aa7da77c0e 100644
--- a/tirx/tests/python/tirx/script/test_ir_builder.py
+++ b/tirx/tests/python/tirx/script/test_ir_builder.py
@@ -25,7 +25,7 @@ import tvm_tirx.testing
from tvm_tirx import tirx
from tvm_tirx.ir.base import assert_structural_equal
from tvm_tirx.ir.location import SourceLoc, SourceName
-from tvm_tirx.script.ir_builder import IRBuilder
+from tvm_tirx.script.ir_builder import AlreadyEmitted, IRBuilder
from tvm_tirx.tirx.layout import S, TileLayout
from tvm_tirx.tirx.script import ir_builder as T
@@ -39,6 +39,116 @@ def _is_tensor_binding(node, *op_names):
)
+def test_alloc_tensor_builder_boundary():
+ tensor_type = T.Tensor((4,), "int32", scope="local")
+ outside = T.alloc_tensor(ty_args=[tensor_type])
+ assert isinstance(outside, tvm_tirx.ir.Call)
+ loc = SourceLoc(SourceName("allocation.py"), 3, 1, 3, 20)
+ with IRBuilder() as ib:
+ with ib.with_loc(loc):
+ raw = tvm_tirx.ir.Call("tirx.alloc_tensor", [],
ty_args=[tensor_type])
+ receipt = T.alloc_tensor(ty_args=[tensor_type],
annotations={"alignment": 16})
+ assert isinstance(receipt, AlreadyEmitted)
+ tensor = T.bind_(receipt, name="tensor", ty=tensor_type,
value_loc=loc)
+ assert tensor.same_as(receipt.value)
+ assert T.bind_(tensor, name="alias").same_as(tensor)
+ assert tensor.name == "tensor"
+ T.emit_(receipt)
+ with pytest.raises(ValueError):
+ T.decl_mutable_cell_(receipt, ty=T.Tensor((8,), "int32",
scope="local"))
+ binding = ib.get()
+ assert isinstance(binding, tvm_tirx.ir.Bind)
+ assert binding.var.same_as(tensor)
+ assert binding.var.ty.same_as(tensor_type)
+ assert binding.var.loc.same_as(loc)
+ assert not binding.value.same_as(raw)
+ assert binding.value.attrs["alignment"].value == 16
+ assert binding.loc.same_as(loc)
+ assert binding.value.loc.same_as(loc)
+
+
[email protected](
+ "producer,scalar",
+ [
+ (lambda: T.alloc_local((4,), "int32"), False),
+ (lambda: T.alloc_shared((4,), "int32"), False),
+ (lambda: T.smem((4,), "int32"), False),
+ (lambda: T.tmem((4,), "int32"), False),
+ (lambda: T.alloc_scalar("int32", "local"), True),
+ (lambda: T.local_scalar("int32"), True),
+ (lambda: T.shared_scalar("int32"), True),
+ (
+ lambda: T.decl_scalar(
+ "int32", tvm_tirx.ir.Var("data", tvm_tirx.ir.PtrType("int32",
"local")), "local"
+ ),
+ True,
+ ),
+ ],
+)
+def test_allocation_convenience_receipts(producer, scalar):
+ with IRBuilder() as ib:
+ receipt = producer()
+ assert isinstance(receipt, AlreadyEmitted)
+ value = T.bind_(receipt, name="storage")
+ assert value.same_as(receipt.value)
+ assert isinstance(value, tvm_tirx.ir.TensorLoad if scalar else
tvm_tirx.ir.Var)
+ assert T.decl_mutable_cell_(receipt, ty=value.ty,
name="storage").same_as(value)
+ T.emit_(receipt)
+ binding = ib.get()
+ assert isinstance(binding, tvm_tirx.ir.Bind)
+ assert binding.var.same_as(value.source if scalar else value)
+ assert binding.var.name == "storage"
+
+
+def test_binding_preserves_void_and_statement_receipts():
+ with IRBuilder() as ib:
+ void = T.call_extern("side_effect", ty="void")
+ statement = T.evaluate(1)
+ for receipt in (void, statement):
+ assert isinstance(receipt, AlreadyEmitted)
+ assert T.bind_(receipt) is receipt
+ assert T.bind_(receipt, ty=T.int32) is receipt
+ T.emit_(receipt)
+ assert len(ib.get().seq) == 2
+
+
+def test_alloc_tensor_parsed_identity():
+ from tvm_tirx.script import tirx as TS
+
+ @TS.function(private=True)
+ def function():
+ tensor = TS.alloc_tensor(ty_args=[TS.Tensor((4,), "int32",
scope="local")])
+ alias = tensor
+ alias[0] = 1
+
+ binding, store = function.body.seq
+ assert _is_tensor_binding(binding, "tirx.alloc_tensor")
+ assert binding.var.name == "tensor"
+ assert store.dest.same_as(binding.var)
+ assert isinstance(binding.var.loc, tvm_tirx.ir.CallSiteLoc)
+ assert binding.var.loc.caller.end_column -
binding.var.loc.caller.start_column == len("tensor")
+ assert isinstance(binding.value.loc, SourceLoc)
+
+
[email protected]("use_let", [False, True])
+def test_alloc_tensor_annotated_binding(use_let):
+ from tvm_tirx.script import tirx as TS
+
+ tensor_type = TS.Tensor((4,), "int32", scope="local")
+ annotation = TS.let[tensor_type] if use_let else tensor_type
+
+ @TS.function(private=True)
+ def function():
+ tensor: annotation = TS.alloc_tensor(ty_args=[TS.Tensor((4,), "int32",
scope="local")])
+ tensor[0] = 1
+
+ binding, store = function.body.seq
+ assert _is_tensor_binding(binding, "tirx.alloc_tensor")
+ assert binding.var.name == "tensor"
+ assert store.dest.same_as(binding.var)
+ assert_structural_equal(binding.var.ty, TS.Tensor((4,), "int32",
scope="local"))
+
+
def test_ir_builder_tir_for():
with IRBuilder() as ib:
with T.serial(128) as a:
@@ -144,10 +254,12 @@ def test_ir_builder_tir_assert():
def test_ir_builder_tir_bind():
- # Test that T.bind emits a flat Bind statement and returns the Var.
+ # Construct and emit a flat Bind statement with an explicit variable.
+ assert T.Bind is tvm_tirx.ir.Bind
with IRBuilder() as ib:
- v = T.bind(tvm_tirx.ir.IntImm("int32", 2))
- # the let binding generated by IRBuilder
+ v = tirx.Var("v", "int32")
+ T.emit_(T.Bind(v, tvm_tirx.ir.IntImm("int32", 2)))
+ # the binding generated by IRBuilder
let_actual = ib.get()
# Bind is now flat (no body), so a single Bind stmt is emitted.
@@ -155,8 +267,19 @@ def test_ir_builder_tir_bind():
# Check if the generated ir is expected
assert_structural_equal(let_actual, let_expected, map_free_vars=True)
- # Check that the returned value is a Var
- assert isinstance(v, tirx.Var)
+ assert let_actual.var.same_as(v)
+
+
+def test_removed_binding_helpers_are_not_exported():
+ from tvm_tirx.ir import stmt
+ from tvm_tirx.script import tirx as TS
+ from tvm_tirx.script.ir_builder import stmt as builder_stmt
+
+ assert not hasattr(T, "bind")
+ assert not hasattr(TS, "bind")
+ assert not hasattr(builder_stmt, "bind")
+ assert not hasattr(tvm_tirx.ir, "stmt_list")
+ assert not hasattr(stmt, "stmt_list")
def test_ir_builder_tir_while():
@@ -331,12 +454,14 @@ def test_concrete_mutable_scalar(declare):
with IRBuilder() as ib:
with T.function():
if declare:
- owner = T.bind(T.alloc_tensor(ty_args=[T.Tensor((4,), "int32",
scope="local")]))
- scalar = T.decl_scalar(
- "int32", owner.data, "local",
layout=T.TileLayout(T.S[(1,)] + (2))
+ owner = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((4,),
"int32", scope="local")]))
+ scalar = T.bind_(
+ T.decl_scalar(
+ "int32", owner.data, "local",
layout=T.TileLayout(T.S[(1,)] + (2))
+ )
)
else:
- scalar = T.alloc_scalar("int32", "local")
+ scalar = T.bind_(T.alloc_scalar("int32", "local"))
assert type(scalar) is tvm_tirx.ir.TensorLoad
named = T.decl_mutable_cell_(scalar, name="counter")
assert named.same_as(scalar)
@@ -369,13 +494,15 @@ def test_local_scalar_initialization_and_assign():
with IRBuilder() as ib:
with T.function():
with ib.with_loc(loc):
- scalar = T.local_scalar("int32", 2, name="counter",
annotations=annotations)
- uninitialized = T.local_scalar()
+ scalar = T.bind_(
+ T.local_scalar("int32", 2, name="counter",
annotations=annotations)
+ )
+ uninitialized = T.bind_(T.local_scalar())
receipt = T.assign(scalar, 3)
assert isinstance(receipt, AlreadyEmitted)
T.emit_(receipt)
source_receipt = T.assign(scalar.source, 4)
- global_tensor = T.bind(T.alloc_tensor(ty_args=[T.Tensor((2,),
"int32")]))
+ global_tensor = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((2,),
"int32")]))
indexed_receipt = T.assign(global_tensor[1], 5)
for invalid in (tirx.Var("immutable", "int32"), scalar + 1,
global_tensor):
with pytest.raises(TypeError, match="scalar storage"):
@@ -465,7 +592,7 @@ def test_ir_builder_tir_allocate():
with IRBuilder() as ib:
with T.function():
T.func_name_("test")
- buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([10], "float32",
scope="local")]))
+ buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([10], "float32",
scope="local")]))
T.evaluate(1)
# the allocate generated by IRBuilder
@@ -484,7 +611,7 @@ def test_ir_builder_tir_decl_tensor():
with IRBuilder() as ib:
with T.function():
T.func_name_("test")
- buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor([128, 128],
"float32")]))
+ buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor([128, 128],
"float32")]))
T.evaluate(1)
# the decl_tensor generated by IRBuilder
diff --git a/tirx/tests/python/tirx/script/test_meta_programming.py
b/tirx/tests/python/tirx/script/test_meta_programming.py
index 5b3e83aa63..f095c44feb 100644
--- a/tirx/tests/python/tirx/script/test_meta_programming.py
+++ b/tirx/tests/python/tirx/script/test_meta_programming.py
@@ -33,7 +33,7 @@ def test_meta_class_constructor_rejects_unowned_resource():
@T.meta_class
class Bad:
def __init__(self):
- tmp = T.bind(T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")]))
+ tmp = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((1,), "int32",
scope="local")]))
with pytest.raises(ValueError):
@@ -50,8 +50,8 @@ def
test_meta_class_multiple_instances_preserve_owned_resources():
class Holder:
def __init__(self, external):
self.external = external
- self.buf = T.bind(T.alloc_tensor(ty_args=[T.Tensor((2,), "int32",
scope="local")]))
- self.scalar = T.local_scalar("int32")
+ self.buf = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((2,), "int32",
scope="local")]))
+ self.scalar = T.bind_(T.local_scalar("int32"))
instances.append(self)
@T.function(private=True)
diff --git a/tirx/tests/python/tirx/script/test_printer.py
b/tirx/tests/python/tirx/script/test_printer.py
index fb0d49d62f..5f68b5811e 100644
--- a/tirx/tests/python/tirx/script/test_printer.py
+++ b/tirx/tests/python/tirx/script/test_printer.py
@@ -256,9 +256,11 @@ def test_allocate(declare):
TB.func_name_("test")
if declare is TB.decl_tensor:
data = TB.ptr("float32")
- buf = TB.bind(declare(data, ty_args=[TB.Tensor([128, 128],
"float32")]))
+ buf_value = declare(data, ty_args=[TB.Tensor([128, 128],
"float32")])
+ buf = tvm_tirx.ir.Var("", buf_value.ty)
+ TB.emit_(TB.Bind(buf, buf_value))
else:
- buf = TB.bind(declare(ty_args=[TB.Tensor([128, 128],
"float32")]))
+ buf = TB.bind_(declare(ty_args=[TB.Tensor([128, 128],
"float32")]))
TB.evaluate(1)
obj = ib.get()
_assert_print(
@@ -281,10 +283,12 @@ def test_allocate_with_decl_tensor_sugar():
with IRBuilder() as ib:
with TB.function():
TB.func_name_("test")
- buf = TB.bind(TB.alloc_tensor(ty_args=[TB.Tensor([128, 128],
"float32")]))
- buf2 = TB.bind(
- TB.decl_tensor(buf.data, ty_args=[TB.Tensor([128, 128],
"float32", data=buf.data)])
+ buf = TB.bind_(TB.alloc_tensor(ty_args=[TB.Tensor([128, 128],
"float32")]))
+ buf2_value = TB.decl_tensor(
+ buf.data, ty_args=[TB.Tensor([128, 128], "float32",
data=buf.data)]
)
+ buf2 = tvm_tirx.ir.Var("", buf2_value.ty)
+ TB.emit_(TB.Bind(buf2, buf2_value))
TB.evaluate(1)
obj = ib.get()
_assert_print(
@@ -302,10 +306,12 @@ def test_allocate_with_decl_tensor_sugar_multi_usage():
with IRBuilder() as ib:
with TB.function():
TB.func_name_("test")
- buf = TB.bind(TB.alloc_tensor(ty_args=[TB.Tensor([128, 128],
"float32")]))
- buf2 = TB.bind(
- TB.decl_tensor(buf.data, ty_args=[TB.Tensor([128, 128],
"float32", data=buf.data)])
+ buf = TB.bind_(TB.alloc_tensor(ty_args=[TB.Tensor([128, 128],
"float32")]))
+ buf2_value = TB.decl_tensor(
+ buf.data, ty_args=[TB.Tensor([128, 128], "float32",
data=buf.data)]
)
+ buf2 = tvm_tirx.ir.Var("", buf2_value.ty)
+ TB.emit_(TB.Bind(buf2, buf2_value))
TB.evaluate(buf.data)
obj = ib.get()
_assert_print(
@@ -322,10 +328,12 @@ def test_allocate_with_decl_tensor_no_sugar_mismatch():
with IRBuilder() as ib:
with TB.function():
TB.func_name_("test")
- buf = TB.bind(TB.alloc_tensor(ty_args=[TB.Tensor([128, 128],
"float32")]))
- buf2 = TB.bind(
- TB.decl_tensor(buf.data, ty_args=[TB.Tensor([256, 256],
"float32", data=buf.data)])
+ buf = TB.bind_(TB.alloc_tensor(ty_args=[TB.Tensor([128, 128],
"float32")]))
+ buf2_value = TB.decl_tensor(
+ buf.data, ty_args=[TB.Tensor([256, 256], "float32",
data=buf.data)]
)
+ buf2 = tvm_tirx.ir.Var("", buf2_value.ty)
+ TB.emit_(TB.Bind(buf2, buf2_value))
TB.evaluate(buf.data)
obj = ib.get()
_assert_print(
@@ -343,12 +351,11 @@ def test_decl_tensor():
with IRBuilder() as ib:
with TB.function():
TB.func_name_("test")
- buf = TB.bind(
- TB.decl_tensor(
- TB.ptr("float32"),
- ty_args=[TB.Tensor((10, 10), "float32",
data=TB.ptr("float32"))],
- )
+ buf_value = TB.decl_tensor(
+ TB.ptr("float32"), ty_args=[TB.Tensor((10, 10), "float32",
data=TB.ptr("float32"))]
)
+ buf = tvm_tirx.ir.Var("", buf_value.ty)
+ TB.emit_(TB.Bind(buf, buf_value))
TB.evaluate(1)
obj = ib.get()
# Print only the body (skip Function wrapper)
diff --git a/tirx/tests/python/tirx/script/test_printer_function.py
b/tirx/tests/python/tirx/script/test_printer_function.py
index 07a0c8d58f..cae67a165a 100644
--- a/tirx/tests/python/tirx/script/test_printer_function.py
+++ b/tirx/tests/python/tirx/script/test_printer_function.py
@@ -179,7 +179,9 @@ def main(A: T.Tensor((128, 128), T.float32), B:
T.Tensor((256, 256), T.float32))
def test_bind():
with IRBuilder() as ib:
with TB.function():
- v = TB.bind(TB.float32(10))
+ v_value = TB.float32(10)
+ v = tvm_tirx.ir.Var("", v_value.ty)
+ TB.emit_(TB.Bind(v, v_value))
ib.name("v", v)
TB.evaluate(1)
obj = ib.get()
diff --git a/tirx/tests/python/tirx/script/test_source_locations.py
b/tirx/tests/python/tirx/script/test_source_locations.py
index 0c8b8f00dc..0f818fdfcc 100644
--- a/tirx/tests/python/tirx/script/test_source_locations.py
+++ b/tirx/tests/python/tirx/script/test_source_locations.py
@@ -228,7 +228,7 @@ def test_explicit_loc_on_global_var_lambda_and_min_max():
assert isinstance(T.max(a, b), prim.Max)
-def test_native_bind_keeps_returned_and_stored_variable_identity():
+def test_explicit_bind_keeps_stored_and_aliased_variable_identity():
# Aliases of an explicitly produced native variable must not rename, reloc
or bind it again.
from tvm_tirx import ir
@@ -247,7 +247,8 @@ def
test_native_bind_keeps_returned_and_stored_variable_identity():
@T.function
def main(x: T.int32):
- renamed = T.bind(argument(x), var=produced)
+ T.Bind(produced, argument(x))
+ renamed = produced
alias = renamed
called = argument(alias)
observe(renamed)
diff --git a/tirx/tests/python/tirx/script/test_tensors.py
b/tirx/tests/python/tirx/script/test_tensors.py
index c383f22add..08753fce8b 100644
--- a/tirx/tests/python/tirx/script/test_tensors.py
+++ b/tirx/tests/python/tirx/script/test_tensors.py
@@ -243,12 +243,12 @@ def test_alloc_apis():
def __init__(self, Ta, inner_pool):
self.Ta = Ta
self.inner_pool = inner_pool
- self.Tb = T.shared_scalar("float16")
- self.idx = T.local_scalar("int32")
- self.inner_pool2 = T.decl_scalar(
+ self.Tb = T.bind_(T.shared_scalar("float16"))
+ self.idx = T.bind_(T.local_scalar("int32"))
+ self.inner_pool2 = T.bind_(T.decl_scalar(
"float16", self.inner_pool.data, "shared.dyn",
layout=T.TileLayout(T.S[(1,)] + 5),
- )
+ ))
@T.inline
def init(self):
@@ -1212,7 +1212,7 @@ def test_tensor_factored_views():
"""Splitting and selecting logical factors preserves the physical
mapping."""
with IRBuilder():
with Tx_builder.function():
- A = Tx_builder.bind(
+ A = Tx_builder.bind_(
Tx_builder.alloc_tensor(
ty_args=[T.Tensor((3, 64, 512), "float16", scope="shared",
layout="default")]
)
diff --git a/tirx/tests/python/tirx/script/test_tracing.py
b/tirx/tests/python/tirx/script/test_tracing.py
index bea151d4ac..e446960bce 100644
--- a/tirx/tests/python/tirx/script/test_tracing.py
+++ b/tirx/tests/python/tirx/script/test_tracing.py
@@ -93,7 +93,7 @@ def test_backend_tile_namespace_emits_once(trace):
with IRBuilder() as builder:
with I.function_():
source = I.arg_("source", T.Tensor((1, 4), T.float32))
- dest = I.bind(T.alloc_tensor(ty_args=[T.Tensor((1, 4), T.float32,
scope="local")]))
+ dest = T.bind_(T.alloc_tensor(ty_args=[T.Tensor((1, 4), T.float32,
scope="local")]))
with builder.line_trace_scope() if trace else nullcontext():
receipt = T.cuda.tile.apply(T.ptx["ld.global.v4.f32"], dest,
source)
assert isinstance(receipt, AlreadyEmitted)
diff --git a/tirx/tests/python/tirx/test_tile_namespace.py
b/tirx/tests/python/tirx/test_tile_namespace.py
index 48997db995..803a944f25 100644
--- a/tirx/tests/python/tirx/test_tile_namespace.py
+++ b/tirx/tests/python/tirx/test_tile_namespace.py
@@ -104,8 +104,10 @@ def test_omitted_and_explicit_none_layout(scope):
with IRBuilder():
with I.function():
tensor_type = I.Tensor((4, 8), "float32", scope=scope)
- allocated = I.bind(I.alloc_tensor(ty_args=[tensor_type]))
- declared = I.bind(I.decl_tensor(allocated.data,
ty_args=[tensor_type]))
+ allocated = I.bind_(I.alloc_tensor(ty_args=[tensor_type]))
+ declared_value = I.decl_tensor(allocated.data,
ty_args=[tensor_type])
+ declared = tvm_tirx.ir.Var("declared", declared_value.ty)
+ I.emit_(I.Bind(declared, declared_value))
assert allocated.layout is None and declared.layout is None
@@ -149,12 +151,12 @@ def test_none_layout_views_keep_offsets_and_strides():
builder = IRBuilder()
with builder:
with I.function():
- source = I.bind(
- I.decl_tensor(
- I.ptr("float32"),
- ty_args=[I.Tensor((4, 8), "float32",
layout=T.TileLayout(T.S[(4, 8)] + (5)))],
- )
+ source_value = I.decl_tensor(
+ I.ptr("float32"),
+ ty_args=[I.Tensor((4, 8), "float32",
layout=T.TileLayout(T.S[(4, 8)] + (5)))],
)
+ source = tvm_tirx.ir.Var("source", source_value.ty)
+ I.emit_(I.Bind(source, source_value))
reshaped = source.view(2, 2, 8)
permuted = source.permute(1, 0)
sliced = source.sub[1:3, 2:6]
@@ -204,7 +206,9 @@ def test_none_layout_dtype_views(dtype, new_dtype,
old_shape, new_shape):
builder = IRBuilder()
with builder:
with I.function():
- source = I.bind(I.decl_tensor(I.ptr(dtype),
ty_args=[I.Tensor(old_shape, dtype)]))
+ source_value = I.decl_tensor(I.ptr(dtype),
ty_args=[I.Tensor(old_shape, dtype)])
+ source = tvm_tirx.ir.Var("source", source_value.ty)
+ I.emit_(I.Bind(source, source_value))
viewed = source.view(new_dtype)
assert viewed.layout is None
assert list(map(int, viewed.shape)) == list(new_shape)
@@ -215,16 +219,18 @@ def test_none_layout_dtype_view_accepts_symbolic_extent():
with IRBuilder():
with I.function():
size = tvm_tirx.tirx.Var("size", "int32")
- source = I.bind(I.decl_tensor(I.ptr("float32"),
ty_args=[I.Tensor((size,), "float32")]))
+ source_value = I.decl_tensor(I.ptr("float32"),
ty_args=[I.Tensor((size,), "float32")])
+ source = tvm_tirx.ir.Var("source", source_value.ty)
+ I.emit_(I.Bind(source, source_value))
viewed = source.view("uint64")
assert viewed.layout is None
assert_structural_equal(viewed.shape[0], size // 2)
- misaligned = I.bind(
- I.decl_tensor(
- I.ptr("float32"),
- ty_args=[I.Tensor((8,), "float32",
layout=T.TileLayout(T.S[(8,)] + (1)))],
- )
+ misaligned_value = I.decl_tensor(
+ I.ptr("float32"),
+ ty_args=[I.Tensor((8,), "float32",
layout=T.TileLayout(T.S[(8,)] + (1)))],
)
+ misaligned = tvm_tirx.ir.Var("misaligned", misaligned_value.ty)
+ I.emit_(I.Bind(misaligned, misaligned_value))
with pytest.raises(ValueError, match="align"):
misaligned.view("uint64")
diff --git a/tirx/tests/python/tirx/tile/cuda/test_ewise.py
b/tirx/tests/python/tirx/tile/cuda/test_ewise.py
index 4661b26d8e..fc5ad16f08 100644
--- a/tirx/tests/python/tirx/tile/cuda/test_ewise.py
+++ b/tirx/tests/python/tirx/tile/cuda/test_ewise.py
@@ -95,7 +95,7 @@ def _shared_case(input, operation, src_dtype, dst_dtype,
bias_type=None, target=
def allocate_output(source):
if in_place:
return source
- return T.alloc_shared(shape, dst_dtype, layout=layout)
+ return T.bind_(T.alloc_shared(shape, dst_dtype, layout=layout))
@T.function
def kernel(
diff --git a/tirx/tests/python/tirx/tile/cuda/test_tcgen05_mma.py
b/tirx/tests/python/tirx/tile/cuda/test_tcgen05_mma.py
index 0355aac865..61648c0e88 100644
--- a/tirx/tests/python/tirx/tile/cuda/test_tcgen05_mma.py
+++ b/tirx/tests/python/tirx/tile/cuda/test_tcgen05_mma.py
@@ -430,7 +430,7 @@ def test_gemm_tcgen05_cta_group_1_layout_f_m64():
def _mapa(ptr, rank):
"""`mapa.u64` into a declared register, returned as a value."""
- mapped = T.alloc_local([1], "uint64", layout="default")
+ mapped = T.bind_(T.alloc_local([1], "uint64", layout="default"))
T.evaluate(T.ptx.mapa.u64(mapped[0], ptr, T.uint32(rank)))
return mapped[0]
diff --git a/tirx/tests/python/tirx/transform/test_common_subexpr_elim.py
b/tirx/tests/python/tirx/transform/test_common_subexpr_elim.py
index ecf25aef86..3d33e08a51 100644
--- a/tirx/tests/python/tirx/transform/test_common_subexpr_elim.py
+++ b/tirx/tests/python/tirx/transform/test_common_subexpr_elim.py
@@ -27,28 +27,28 @@ def test_basic():
class Before:
@T.function
def main(B: T.Tensor((50,), "int32"), i1: T.int32, i2: T.int32, z3:
T.int32):
- z1 = T.bind(1)
- z2 = T.bind(2)
+ z1 = 1
+ z2 = 2
B[i1] = z1 + z2
- x = T.bind(1)
- y = T.bind(1)
- a = T.bind((x + y) + (z1 + z2))
- b = T.bind((x + y) + z3)
+ x = 1
+ y = 1
+ a = (x + y) + (z1 + z2)
+ b = (x + y) + z3
B[i2] = a + b
@tvm_tirx.script.ir_module
class Expected:
@T.function
def main(B: T.Tensor((50,), "int32"), i1: T.int32, i2: T.int32, z3:
T.int32):
- z1 = T.bind(1)
- z2 = T.bind(2)
- cse_v1 = T.bind(z1 + z2)
+ z1 = 1
+ z2 = 2
+ cse_v1 = z1 + z2
B[i1] = cse_v1
- x = T.bind(1)
- y = T.bind(1)
- cse_v2 = T.bind(x + y)
- a = T.bind(cse_v2 + cse_v1)
- b = T.bind(cse_v2 + z3)
+ x = 1
+ y = 1
+ cse_v2 = x + y
+ a = cse_v2 + cse_v1
+ b = cse_v2 + z3
B[i2] = a + b
after = tvm_tirx.tirx.transform.CommonSubexprElim()(Before)
@@ -67,7 +67,7 @@ def test_if_single_branch():
y: T.int32,
z: T.int32,
):
- b = T.bind(1)
+ b = 1
if b:
B[i1] = y + z
B[i2] = y + z
@@ -85,9 +85,9 @@ def test_if_single_branch():
y: T.int32,
z: T.int32,
):
- b = T.bind(1)
+ b = 1
if b:
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
B[i1] = cse_v1
B[i2] = cse_v1
else:
@@ -109,7 +109,7 @@ def test_if_both_branches():
y: T.int32,
z: T.int32,
):
- b = T.bind(1)
+ b = 1
if b:
B[i1] = y + z
B[i2] = y
@@ -127,8 +127,8 @@ def test_if_both_branches():
y: T.int32,
z: T.int32,
):
- b = T.bind(1)
- cse_v1 = T.bind(y + z)
+ b = 1
+ cse_v1 = y + z
if b:
B[i1] = cse_v1
B[i2] = y
@@ -168,8 +168,8 @@ def test_cascade():
y: T.int32,
z: T.int32,
):
- cse_v2 = T.bind(x + y)
- cse_v1 = T.bind(cse_v2 + z)
+ cse_v2 = x + y
+ cse_v1 = cse_v2 + z
B[i1] = cse_v1
B[i2] = cse_v1
B[i3] = cse_v2
@@ -183,7 +183,7 @@ def test_no_duplication():
class Before:
@T.function
def main(x: T.int32, y: T.int32, z: T.int32):
- a = T.bind(x + (y + z))
+ a = x + (y + z)
T.evaluate(a)
Expected = Before
@@ -206,9 +206,9 @@ def test_deterministic():
class Expected:
@T.function
def main(B: T.Tensor((4,), "int32"), x: T.int32):
- cse_v1 = T.bind(x + 1)
+ cse_v1 = x + 1
B[0] = cse_v1
- cse_v2 = T.bind(x + 2)
+ cse_v2 = x + 2
B[1] = cse_v2
B[2] = cse_v1
B[3] = cse_v2
@@ -235,7 +235,7 @@ def test_for_loop():
@T.function
def main(B: T.Tensor((50,), "int32"), y: T.int32, z: T.int32):
for i in range(10):
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
B[i] = cse_v1
B[i + 10] = cse_v1
@@ -256,7 +256,7 @@ def test_for_hoist():
class Expected:
@T.function
def main(B: T.Tensor((50,), "int32"), y: T.int32, z: T.int32):
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
B[0] = cse_v1
for i in range(10):
B[i + 1] = cse_v1
@@ -309,7 +309,7 @@ def test_nested_if():
z: T.int32,
):
if c1:
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
if c2:
B[0] = cse_v1
else:
@@ -347,9 +347,9 @@ def test_multi_independent():
c: T.int32,
d: T.int32,
):
- cse_v1 = T.bind(a + b)
+ cse_v1 = a + b
B[0] = cse_v1
- cse_v2 = T.bind(c + d)
+ cse_v2 = c + d
B[1] = cse_v2
B[2] = cse_v1
B[3] = cse_v2
@@ -370,7 +370,7 @@ def test_if_condition():
class Expected:
@T.function
def main(B: T.Tensor((50,), "int32"), y: T.int32, z: T.int32):
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
if cse_v1 > 0:
B[0] = cse_v1
@@ -414,7 +414,7 @@ def test_no_single_use_binding():
y: T.int32,
z: T.int32,
):
- cse_v1 = T.bind((x + y) + z)
+ cse_v1 = (x + y) + z
B[0] = cse_v1
B[1] = cse_v1
@@ -434,7 +434,7 @@ def test_for_extent_lift():
class Expected:
@T.function
def main(B: T.Tensor((50,), "int32"), y: T.int32, z: T.int32):
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
for i in range(cse_v1):
B[i] = cse_v1
@@ -461,7 +461,7 @@ def test_loop_var_expr_stays_inside():
B: T.Tensor((50,), "int32"),
):
for i in range(10):
- cse_v1 = T.bind(i * 4)
+ cse_v1 = i * 4
A[cse_v1] = B[cse_v1]
after = tvm_tirx.tirx.transform.CommonSubexprElim()(Before)
@@ -497,7 +497,7 @@ def test_let_value_cse():
@T.function
def main(B: T.Tensor((10,), "int32"), y: T.int32, z: T.int32):
for i in range(10):
- cse_v1 = T.bind(y + z)
+ cse_v1 = y + z
B[i] = cse_v1 + T.Let(x + 1, where={x: cse_v1})
After = tvm_tirx.tirx.transform.CommonSubexprElim()(Before)
@@ -598,15 +598,15 @@ def test_shared_subtree_stays_ssa():
class Expected:
@T.function
def main(B: T.Tensor((50,), "int32"), i1: T.int32, i2: T.int32):
- value = T.bind(i1 + i2)
- twice = T.bind(value * 2)
+ value = i1 + i2
+ twice = value * 2
B[i1] = twice
- thrice = T.bind(value * 3)
+ thrice = value * 3
B[i2] = thrice
- value2 = T.bind(i1 + i2)
- twice2 = T.bind(value2 * 2)
+ value2 = i1 + i2
+ twice2 = value2 * 2
B[i1] = twice2
- thrice2 = T.bind(value * 3)
+ thrice2 = value * 3
B[i2] = thrice2
unrolled = tvm_tirx.tirx.transform.UnrollLoop()(Before)
diff --git a/tirx/tests/python/tirx/transform/test_convert_ssa.py
b/tirx/tests/python/tirx/transform/test_convert_ssa.py
index 632adbb8ad..0ef8c45acf 100644
--- a/tirx/tests/python/tirx/transform/test_convert_ssa.py
+++ b/tirx/tests/python/tirx/transform/test_convert_ssa.py
@@ -32,18 +32,18 @@ def test_reuse_in_sequential_bind():
class Before:
@T.function(private=True)
def main():
- T.bind(16, var=var)
+ T.Bind(var, 16)
T.evaluate(var)
- T.bind(32, var=var)
+ T.Bind(var, 32)
T.evaluate(var)
assert Before["main"].body[0].var.same_as(Before["main"].body[2].var)
@T.function(private=True)
def expected():
- var1 = T.bind(T.int32(16))
+ var1 = T.int32(16)
T.evaluate(var1)
- var2 = T.bind(T.int32(32))
+ var2 = T.int32(32)
T.evaluate(var2)
After = tvm_tirx.tirx.transform.ConvertSSA()(Before)
@@ -58,9 +58,9 @@ def test_reuse_in_nested_bind():
class Before:
@T.function(private=True)
def main():
- T.bind(32, var=var)
+ T.Bind(var, 32)
T.evaluate(var)
- T.bind(16, var=var)
+ T.Bind(var, 16)
T.evaluate(var)
T.evaluate(var)
@@ -68,9 +68,9 @@ def test_reuse_in_nested_bind():
class Expected:
@T.function(private=True)
def main():
- var1 = T.bind(32)
+ var1 = 32
T.evaluate(var1)
- var2 = T.bind(16)
+ var2 = 16
T.evaluate(var2)
T.evaluate(var2)
@@ -84,7 +84,7 @@ def test_reused_var_across_module():
@T.function(private=True)
def func():
- var = T.bind(10)
+ var = 10
T.evaluate(var)
@I.ir_module(check_well_formed=False)
diff --git a/tirx/tests/python/tirx/transform/test_function_pass.py
b/tirx/tests/python/tirx/transform/test_function_pass.py
index 6a14f8c483..c2fffb88ba 100644
--- a/tirx/tests/python/tirx/transform/test_function_pass.py
+++ b/tirx/tests/python/tirx/transform/test_function_pass.py
@@ -26,7 +26,7 @@ def test_function_pass(capsys):
class Before:
@T.function(private=True)
def main(x: T.int32, y: T.int32, B: T.Tensor((16,), "float32")):
- value = T.bind(10)
+ value = 10
T.evaluate(value + 1)
@I.ir_module
diff --git a/tirx/tests/python/tirx/transform/test_lower_tensor_layouts.py
b/tirx/tests/python/tirx/transform/test_lower_tensor_layouts.py
index edae515ca5..91b4708449 100644
--- a/tirx/tests/python/tirx/transform/test_lower_tensor_layouts.py
+++ b/tirx/tests/python/tirx/transform/test_lower_tensor_layouts.py
@@ -1275,15 +1275,17 @@ def test_lower_alloc_decl_tensor_outside_of_parser():
@T.meta_class
class State:
def __init__(self, smem):
- self.A = T.alloc_local([1], "float16")
- self.B = T.alloc_local([1], "float16")
- self.C = T.bind(
- T.decl_tensor(smem, ty_args=[T.Tensor([1], "float16",
scope="shared.dyn")])
+ self.A = T.bind_(T.alloc_local([1], "float16"))
+ self.B = T.bind_(T.alloc_local([1], "float16"))
+ declaration = T.decl_tensor(
+ smem, ty_args=[T.Tensor([1], "float16", scope="shared.dyn")]
)
+ self.C = tvm_tirx.ir.Var("C", declaration.ty)
+ T.emit_(T.Bind(self.C, declaration))
@register_mutable_decl("TestMutableCells.int_var1")
def int_var1(val):
- buf = T.local_scalar("int32")
+ buf = T.bind_(T.local_scalar("int32"))
if val is not None:
T.tensor_store(buf.source, 0, val)
return buf
@@ -1292,7 +1294,7 @@ def test_lower_alloc_decl_tensor_outside_of_parser():
register_namespace("TestMutableCells", TestMutableCells)
def int_var2(val):
- buf = T.alloc_local([1], "int32")
+ buf = T.bind_(T.alloc_local([1], "int32"))
if val is not None:
T.tensor_store(buf, 0, val)
return buf