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 9b02ce500f [FIX][Codegen] Bind tensor views with their element pointer
type (#20657)
9b02ce500f is described below
commit 9b02ce500f0801f7427c94392e9a9b4d58382f88
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun Oct 11 11:07:00 2026 -0400
[FIX][Codegen] Bind tensor views with their element pointer type (#20657)
Tensor aliases backed by untyped or differently typed pointers currently
repeat casts at every access. Declare a typed view once and reuse it for
matching accesses, while preserving backing allocations, address spaces,
volatile accesses and required vector/packed casts. Reuse packed
argument decoding casts and keep WebGPU aliases on their existing WGSL
array representation.
---
src/backend/webgpu/codegen/codegen_webgpu.cc | 15 ++++++++++++
src/backend/webgpu/codegen/codegen_webgpu.h | 1 +
src/target/source/codegen_c.cc | 29 +++++++++++++++-------
tests/python/tirx/codegen/test_codegen_cuda.py | 33 ++++++++++++++++++++++++++
4 files changed, 69 insertions(+), 9 deletions(-)
diff --git a/src/backend/webgpu/codegen/codegen_webgpu.cc
b/src/backend/webgpu/codegen/codegen_webgpu.cc
index ee2c92d2f3..20f26360aa 100644
--- a/src/backend/webgpu/codegen/codegen_webgpu.cc
+++ b/src/backend/webgpu/codegen/codegen_webgpu.cc
@@ -769,6 +769,21 @@ void CodeGenWebGPU::Dispatch_(const TensorStoreNode* op) {
}
}
+void CodeGenWebGPU::DispatchDeclTensor(const BindNode* op, const CallNode*
buffer_call) {
+ Expr data = buffer_call->args[0];
+ const VarNode* source = data.as<VarNode>();
+ if (const auto* call = data.as<CallNode>();
+ call && call->op.same_as(tirx::tensor_data_ptr_op()) &&
call->args.size() == 1) {
+ source = call->args[0].as<VarNode>();
+ }
+ TVM_FFI_CHECK(source && var_idmap_.count(source), ValueError)
+ << "WebGPU tensor views require a named storage array";
+ // WGSL storage is an array, not a C pointer. Preserve its physical element
+ // representation and access mode by sharing the existing array name.
+ TVM_FFI_ICHECK(!var_idmap_.count(op->var.get()));
+ var_idmap_[op->var.get()] = GetVarID(source);
+}
+
void CodeGenWebGPU::DispatchAllocTensor(const BindNode* op, const CallNode*
buffer_call) {
tvm::Tuple shape = buffer_call->args[0].as_or_throw<tvm::Tuple>();
DLDataType dtype = buffer_call->args[1].as_or_throw<DataTypeImm>()->value;
diff --git a/src/backend/webgpu/codegen/codegen_webgpu.h
b/src/backend/webgpu/codegen/codegen_webgpu.h
index 92f967ec17..90ccc28014 100644
--- a/src/backend/webgpu/codegen/codegen_webgpu.h
+++ b/src/backend/webgpu/codegen/codegen_webgpu.h
@@ -84,6 +84,7 @@ class CodeGenWebGPU final : public CodeGenC {
void Dispatch_(const TensorStoreNode* op) final;
void Dispatch_(const ForNode* op) final;
void DispatchAllocTensor(const BindNode* op, const CallNode* buffer_call);
+ void DispatchDeclTensor(const BindNode* op, const CallNode* buffer_call);
void Dispatch_(const AssertStmtNode* op) final;
void Dispatch_(const WhileNode* op) final;
void Dispatch_(const BreakNode* op) final;
diff --git a/src/target/source/codegen_c.cc b/src/target/source/codegen_c.cc
index 6eef5cf4c9..ae2271e41d 100644
--- a/src/target/source/codegen_c.cc
+++ b/src/target/source/codegen_c.cc
@@ -23,6 +23,7 @@
#include "codegen_c.h"
#include <tvm/ffi/cast.h>
+#include <tvm/ffi/extra/structural_equal.h>
#include <tvm/ir/function.h>
#include <tvm/ir/prim/op.h>
#include <tvm/ir/unique_name_supply.h>
@@ -946,18 +947,20 @@ void CodeGenC::DispatchDeclTensor(const BindNode* op,
const CallNode* buffer_cal
}
if (source && var_idmap_.count(source)) {
TVM_FFI_ICHECK(!var_idmap_.count(buffer.get()));
- var_idmap_[buffer.get()] = GetVarID(source);
if (auto it = alloc_storage_scope_.find(source); it !=
alloc_storage_scope_.end()) {
- alloc_storage_scope_[buffer.get()] = it->second;
- } else {
- alloc_storage_scope_[buffer.get()] = scope;
+ scope = it->second;
}
if (IsVolatile(source)) {
MarkVolatile(buffer.get());
}
- auto it = handle_data_type_.find(source);
- RegisterHandleType(buffer.get(), it == handle_data_type_.end() ?
PrimType(dtype) : it->second);
- return;
+ // Reuse the name only when it already has the view's element type.
+ // Otherwise declare a typed view without changing the backing allocation.
+ if (HandleTypeMatch(source, PrimType(dtype))) {
+ var_idmap_[buffer.get()] = GetVarID(source);
+ alloc_storage_scope_[buffer.get()] = scope;
+ RegisterHandleType(buffer.get(), PrimType(dtype));
+ return;
+ }
}
alloc_storage_scope_[buffer.get()] = scope;
@@ -965,9 +968,17 @@ void CodeGenC::DispatchDeclTensor(const BindNode* op,
const CallNode* buffer_cal
if (IsScopePartOfType()) {
PrintStorageScope(scope, stream);
}
- PrintType(PtrType(PrimType(dtype), scope), stream);
+ PtrType pointer_type(PrimType(dtype), scope);
+ PrintType(pointer_type, stream);
stream << ' ' << AllocVarID(buffer.get()) << " = ";
- PrintExpr(Call(PtrType(PrimType(dtype), scope), tirx::reinterpret_op(),
{data}), stream);
+ // Packed argument decoding may already have established this typed view.
+ const auto* cast = data.as<CallNode>();
+ if (cast && cast->op.same_as(tirx::reinterpret_op()) &&
+ ffi::StructuralEqual()(cast->ty, pointer_type)) {
+ PrintExpr(data, stream);
+ } else {
+ PrintExpr(Call(pointer_type, tirx::reinterpret_op(), {data}), stream);
+ }
stream << ";\n";
RegisterHandleType(buffer.get(), PrimType(dtype));
}
diff --git a/tests/python/tirx/codegen/test_codegen_cuda.py
b/tests/python/tirx/codegen/test_codegen_cuda.py
index a1fc56db32..31e72be560 100644
--- a/tests/python/tirx/codegen/test_codegen_cuda.py
+++ b/tests/python/tirx/codegen/test_codegen_cuda.py
@@ -152,6 +152,39 @@ def test_cuda_module_destructor_preserves_current_device():
torch.cuda.set_device(original_device)
+def test_typed_buffer_bindings(monkeypatch):
+ @T.function
+ def main(data: T.handle("float32"), output: T.handle):
+ T.func_attr({"global_symbol": "main", "calling_conv": 2,
"tirx.kernel_launch_params": []})
+ A = T.decl_tensor((1,), "float32", data=data)
+ B = T.decl_tensor((1,), "float32", data=output)
+ storage = T.alloc_tensor((1,), "uint32", scope="shared")
+ S = T.decl_tensor((1,), "float32", data=storage.data, scope="shared")
+ pool = T.alloc_tensor((4,), "uint8", scope="shared.dyn")
+ D = T.decl_tensor((1,), "float32", data=pool.data, scope="shared.dyn")
+ alias = T.decl_tensor((1,), "float32", data=D.data, scope="shared.dyn")
+ S[0] = A[0]
+ D[0] = S[0]
+ B[0] = alias[0]
+ storage[0] = T.uint32(0)
+
+ monkeypatch.setenv("TVM_COMPILE_FORCE_FALLBACK", "1")
+ target = tvm.target.Target({"kind": "cuda", "arch": "sm_80"})
+ source = tvm.get_global_func("target.build.cuda")(
+ tvm.IRModule.from_expr(main), target
+ ).inspect_source()
+ assert "float* data, void* output" in source
+ assert "float* B = ((float*)output);" in source
+ assert "__shared__ alignas(64) uint storage_ptr[1];" in source
+ assert "float* S = ((float*)storage_ptr);" in source
+ assert "extern __shared__ __align__(64) uchar pool_ptr[];" in source
+ assert "float* D = ((float*)pool_ptr);" in source
+ assert "S[0] = data[0];" in source
+ assert "D[0] = S[0];" in source
+ assert "B[0] = D[0];" in source
+ assert "storage_ptr[0] = (uint)0;" in source
+
+
def test_vector_pointer_preserves_packed_offset(monkeypatch):
tensor = tvm.tirx.decl_tensor((8,), "int4x4", name="A")
data = tvm.tirx.Var("A_data", tensor.data.ty)