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 7dc7f92403 refactor(op-dispatch): unify thread scopes as enum tags
(#20732)
7dc7f92403 is described below
commit 7dc7f92403d8da2258d2ef99ce099f3821af77e0
Author: Bohan Hou <[email protected]>
AuthorDate: Wed Oct 14 18:37:24 2026 -0400
refactor(op-dispatch): unify thread scopes as enum tags (#20732)
Thread scopes currently use a separate object registry, while tile Calls
store a string and recreate the scope during decoding and dispatch.
Replace these representations with canonical `ffi.Enum` tags shared by
Call attributes, Axis/Layout, axis fusers, and
`DispatchContext.thread_scope`.
CUDA and Trainium register their tags natively with explicit backend and
hierarchy-rank attributes. Rank comparisons are independent of enum
registration order. Instruction constructors resolve short names,
qualified names, or enum values before creating a Call: `scope="warp"`
now stores `ThreadScope("cuda.warp")`. Raw attribute construction
requires a scope tag or a registered qualified name.
Update TVMScript printing, JSON/pickle singleton round-trips, backend
helpers, documentation, and regression tests. Remove the former
`ExecScope` API and registration hooks; serialized data using the old
scope type must be regenerated. Native C++ tests cover registration,
rank ordering, typed attributes, and dispatch without Python.
Validation:
- Build and pre-commit pass.
- 304 targeted Python regressions pass with the repository-pinned
tvm-ffi: ThreadScope/Axis/Layout, Call validation and dispatch, CUDA
execution contexts, Trainium private allocation, IRBuilder void
emission, GPU script, printer snapshots, and JSON round-trips. This
includes the three printer expectations identified by the first CPU CI
run and updated for canonical enum tags.
- An earlier extended CUDA/TRN/script run had 571 passing cases and 3
skips. Its two JSON failures, together with three initial core JSON
failures, all pass with the pinned tvm-ffi. The original local
environment used the older 0.1.14.post1 release; a temporary
source-matched 0.1.15rc0 build resolves the dtype corruption.
- Two native C++ tests pass without loading Python and also passed in
CPU CI.
- Pinned benchmark workload import check passes for 93 kernels. Broader
downstream imports encounter existing references to the absent
`tvm.tirx.script.builder` and `IRBuilder._set_current_loc` APIs.
---
docs/reference/api/python/script/script.rst | 2 +-
docs/tirx/api/execution.rst | 37 ++++--
docs/tirx/api/tile_dispatch.rst | 24 ++--
docs/tirx/api/tirx.rst | 2 +-
docs/tirx/arch/tile_dispatch.rst | 8 +-
include/tvm/tirx/layout.h | 10 +-
include/tvm/tirx/script/ir_builder/frame.h | 2 +-
include/tvm/tirx/script/ir_builder/ir.h | 4 +-
include/tvm/tirx/stmt.h | 2 +-
include/tvm/tirx/tile/context.h | 18 +--
include/tvm/tirx/tile/exec_scope.h | 57 --------
include/tvm/tirx/tile/thread_scope.h | 82 ++++++++++++
python/tvm/backend/cuda/__init__.py | 7 +-
python/tvm/backend/cuda/script.py | 2 +-
.../{trn/exec_scope.py => cuda/thread_scope.py} | 18 +--
python/tvm/backend/trn/__init__.py | 11 +-
.../{cuda/exec_scope.py => trn/thread_scope.py} | 18 +--
python/tvm/backend/trn/tile/binary/default.py | 2 +-
python/tvm/backend/trn/tile/copy/default.py | 2 +-
python/tvm/backend/trn/tile/gemm/default.py | 2 +-
python/tvm/backend/trn/tile/reduction/utils.py | 2 +-
python/tvm/backend/trn/tile/select/default.py | 2 +-
python/tvm/backend/trn/tile/unary/default.py | 2 +-
.../tvm/backend/trn/tile/unary/with_bias_scale.py | 2 +-
.../backend/trn/transform/private_tensor_alloc.py | 6 +-
python/tvm/tirx/__init__.py | 2 +-
python/tvm/tirx/exec_scope.py | 45 -------
python/tvm/tirx/layout.py | 8 +-
python/tvm/tirx/script/ir_builder/ir.py | 4 +-
python/tvm/tirx/thread_scope.py | 64 +++++++++
python/tvm/tirx/tile/dispatch.py | 10 +-
python/tvm/tirx/tile/instruction.py | 35 +++--
src/backend/cuda/codegen/target_kind.cc | 12 ++
src/backend/cuda/layout/axis.cc | 87 +++++++------
src/backend/cuda/tile/exec_analysis.cc | 10 +-
src/backend/cuda/tile/exec_analysis.h | 4 +-
src/backend/cuda/tile/exec_context.h | 2 +-
.../cuda/tile/{exec_scope.cc => thread_scope.cc} | 2 +-
.../cuda/tile/{exec_scope.h => thread_scope.h} | 6 +-
src/backend/trn/codegen/target_kind.cc | 7 +
src/tirx/ir/layout/axis_registry.cc | 8 +-
src/tirx/ir/layout/tile_canonicalize.cc | 4 +-
src/tirx/ir/layout/tile_core.cc | 45 +++----
src/tirx/script/ir_builder/frame.cc | 2 +-
src/tirx/script/ir_builder/ir.cc | 2 +-
src/tirx/script/printer/block.cc | 15 +--
src/tirx/script/printer/expr.cc | 4 +-
src/tirx/tile/context.cc | 16 ++-
src/tirx/tile/exec_scope.cc | 68 ----------
src/tirx/tile/lower.cc | 12 +-
src/tirx/tile/op.cc | 12 +-
src/tirx/tile/thread_scope.cc | 80 ++++++++++++
tests/cpp/tirx/thread_scope_test.cc | 108 ++++++++++++++++
.../tirx/script/test_tirx_script_basic_usage.py | 18 ++-
tests/python/tirx/script/test_tirx_script_gpu.py | 2 +-
.../python/tirx/script/test_tirx_script_printer.py | 3 +-
tests/python/tirx/test_exec_scope.py | 43 -------
tests/python/tirx/test_layout.py | 24 ++--
tests/python/tirx/test_op.py | 2 +-
tests/python/tirx/test_thread_scope.py | 143 +++++++++++++++++++++
tests/python/tirx/test_tile_namespace.py | 24 ++--
tests/python/tirx/test_tirx_axis_enum.py | 4 +-
.../python/tirx/tile/cuda/test_add_sub_mul_div.py | 48 +++----
tests/python/tirx/tile/cuda/test_cp_async_bulk.py | 6 +-
.../tirx/tile/cuda/test_cp_async_bulk_tensor.py | 4 +-
tests/python/tirx/tile/cuda/test_mov.py | 18 +--
tests/python/tirx/tile/test_dispatcher.py | 27 +++-
tests/python/tirx/tile/trn/test_binary_trn.py | 6 +-
tests/python/tirx/tile/trn/test_compose_op_trn.py | 6 +-
tests/python/tirx/tile/trn/test_copy_trn.py | 6 +-
tests/python/tirx/tile/trn/test_gemm_trn.py | 6 +-
tests/python/tirx/tile/trn/test_reduction_trn.py | 6 +-
tests/python/tirx/tile/trn/test_select_trn.py | 6 +-
tests/python/tirx/tile/trn/test_unary_trn.py | 6 +-
74 files changed, 871 insertions(+), 535 deletions(-)
diff --git a/docs/reference/api/python/script/script.rst
b/docs/reference/api/python/script/script.rst
index 18a614e83a..dd287b4899 100644
--- a/docs/reference/api/python/script/script.rst
+++ b/docs/reference/api/python/script/script.rst
@@ -58,7 +58,7 @@ The canonical ``MemoryScope`` class is documented in
:mod:`tvm.runtime`.
:members:
:exclude-members: AlreadyEmitted, DataTypeImm, FuncType, GenericConst,
IRBuilder,
IRModuleFrame, MemoryScope, MissingType, Op, PrimType, StringImm,
StringType, TensorRegion,
- TensorRegionType, Type, Range, meta_var, Var, Call, CommReducer, Reduce,
FloatImm, IntImm, Cast, Add, Sub, Mul, Div, Mod, FloorDiv, FloorMod, LShift,
RShift, BitwiseAnd, BitwiseOr, BitwiseXor, BitwiseNot, Min, Max, EQ, NE, LT,
LE, GT, GE, And, Or, Not, Select, Ramp, Broadcast, Shuffle, CallEffectKind,
IterVar, ComposeLayout, DtypeConstructor, ExecScope, Iter, Layout,
LetAnnotation, LocalVectorAnnotation, TileLayout, CallFFIKernelAttr,
TensorMapEncodeTiledAttr, function, jit, inline, macro
+ TensorRegionType, Type, Range, meta_var, Var, Call, CommReducer, Reduce,
FloatImm, IntImm, Cast, Add, Sub, Mul, Div, Mod, FloorDiv, FloorMod, LShift,
RShift, BitwiseAnd, BitwiseOr, BitwiseXor, BitwiseNot, Min, Max, EQ, NE, LT,
LE, GT, GE, And, Or, Not, Select, Ramp, Broadcast, Shuffle, CallEffectKind,
IterVar, ComposeLayout, DtypeConstructor, ThreadScope, Iter, Layout,
LetAnnotation, LocalVectorAnnotation, TileLayout, CallFFIKernelAttr,
TensorMapEncodeTiledAttr, function, jit, inli [...]
.. autofunction:: tvm.script.tirx.function
diff --git a/docs/tirx/api/execution.rst b/docs/tirx/api/execution.rst
index 24c8273f68..6c22eebb30 100644
--- a/docs/tirx/api/execution.rst
+++ b/docs/tirx/api/execution.rst
@@ -15,32 +15,47 @@
specific language governing permissions and limitations
under the License.
-Execution Scope IR
-==================
+Thread Scope Enum Tags
+======================
-tvm.tirx.exec_scope
--------------------
-.. automodule:: tvm.tirx.exec_scope
+tvm.tirx.thread_scope
+---------------------
+.. automodule:: tvm.tirx.thread_scope
:members:
:no-index:
Backend scopes
--------------
-Execution scope identities remain part of generic layout IR. Backends register
-their finite execution hierarchies and provide the user-facing constructors:
-``T.cuda.ExecScope("warp")`` and ``T.trn.ExecScope("thread")``.
-``T.cuda.ExecScope("kernel")`` names the root used by grid axes such as ``bx``
+Thread scopes are canonical ``ffi.Enum`` identities shared by layout IR,
+tile Call attributes, and dispatch contexts. Query a registered tag with
+``T.ThreadScope("cuda.warp")``. The ``name`` property is the qualified enum
+name; ``local_name`` is ``"warp"``. ``backend`` and ``rank`` are enum
attributes,
+and registration order has no hierarchy semantics.
+
+Backends register their finite thread hierarchies natively using ``EnumDef``
+and explicit backend and rank attributes. Their user-facing lookup helpers are:
+``T.cuda.ThreadScope("warp")`` and ``T.trn.ThreadScope("thread")``.
+``T.cuda.ThreadScope("kernel")`` names the root used by grid axes such as
``bx``
in the ``kernel -> cta`` relationship. Every scope uniformly describes a
hierarchy node; each backend tile instruction declares the scopes it supports.
+Same-backend hierarchy comparisons use explicit ranks; cross-backend
comparisons
+are rejected.
For example, CUDA TMA instructions accept ``thread`` and ``tcgen05.ld/st``
accept
``warpgroup``. CUDA index bindings and predicate analysis live in
``tvm.backend.cuda.tile.exec_context``.
-.. automodule:: tvm.backend.cuda.exec_scope
+Instruction APIs continue accepting ``scope="warp"`` and also accept qualified
+names or ``ThreadScope`` values. Construction resolves the value before
+creating the Call; ``Call.attrs.scope`` always contains a ``ThreadScope`` enum.
+Omitting scope uses the instruction backend's ``thread`` tag. Direct attribute
+construction must provide the tag or its qualified name; shared attribute
+schemas have no backend-independent default.
+
+.. automodule:: tvm.backend.cuda.thread_scope
:members:
:no-index:
-.. automodule:: tvm.backend.trn.exec_scope
+.. automodule:: tvm.backend.trn.thread_scope
:members:
:no-index:
diff --git a/docs/tirx/api/tile_dispatch.rst b/docs/tirx/api/tile_dispatch.rst
index c48ba7466c..7feed70fe4 100644
--- a/docs/tirx/api/tile_dispatch.rst
+++ b/docs/tirx/api/tile_dispatch.rst
@@ -43,7 +43,9 @@ registers an adapter that decodes the semantic view and
invokes its lowerer.
Custom implementations can register the callback directly using
``tvm.ir.register_op_attr(op_name, "FTileOpDispatch", dispatch)`` or C++
``OpDef::set_attr<tile::FTileOpDispatch>``. The Call must carry reflected
-attributes with a string ``scope`` field in the target's execution hierarchy.
+attributes with a required ``ThreadScope`` ``scope`` field in the target's
+execution hierarchy. Direct construction may use a registered qualified name,
+such as ``"cuda.thread"``, which the typed FFI boundary resolves to its enum.
Named workspace operands have fixed optional slots. Declare a workspace policy
only for instructions that own such scratch storage; do not use it to hide
@@ -55,13 +57,15 @@ The common Python API is under ``tvm.tirx.tile`` (also
``T.tile``):
``Instruction``, ``Operand``, ``TensorCall``, and ``DispatchContext``. Backend
implementations live under ``tvm.backend.cuda.tile`` and
``tvm.backend.trn.tile``. Their user-facing instruction paths remain
-``T.cuda.tile`` and ``T.trn.tile``. Operator names and serialized type keys
-remain unchanged.
+``T.cuda.tile`` and ``T.trn.tile``. Instruction operator names and attribute
+schema type keys remain unchanged. Scope tags use the ``tirx.ThreadScope`` type
+key; serialized data containing the former scope type must be regenerated.
-Execution scopes are generic backend/name identities. Backend load hooks
-register their supported scopes and ordering; CUDA exposes
-``T.cuda.ExecScope("warp")`` and Trainium exposes
-``T.trn.ExecScope("thread")``. A string instruction scope is resolved by the
-instruction's backend. CUDA predicate analysis, index bindings, and active
-thread sets live in its private tile implementation; Trainium does not use
-CUDA's execution-context model.
+Thread scopes are backend-qualified enum identities. Native backends register
+their supported tags and explicit ranks with ``EnumDef``; CUDA exposes
+``T.cuda.ThreadScope("warp")`` and Trainium exposes
+``T.trn.ThreadScope("thread")``. A string instruction scope is resolved by the
+instruction's backend before constructing its Call. Lowerers consume the enum
+directly through ``DispatchContext.thread_scope``. CUDA predicate analysis,
+index bindings, and active thread sets live in its private tile implementation;
+Trainium does not use CUDA's execution-context model.
diff --git a/docs/tirx/api/tirx.rst b/docs/tirx/api/tirx.rst
index 033fdcbd4e..f02bfe7720 100644
--- a/docs/tirx/api/tirx.rst
+++ b/docs/tirx/api/tirx.rst
@@ -33,6 +33,6 @@ from ``tvm/ir/type.h``.
:members:
:imported-members:
:exclude-members: Call, CommReducer, ComposeLayout, DispatchContext,
- ExecScope, Expr, Layout, Op, PrimExpr, Reduce,
+ ThreadScope, Expr, Layout, Op, PrimExpr, Reduce,
TileLayout, Var, build, const,
get_default_tir_pipeline, get_tir_pipeline, register_tir_pipeline
diff --git a/docs/tirx/arch/tile_dispatch.rst b/docs/tirx/arch/tile_dispatch.rst
index 4e000d1e7f..8d2fa2522b 100644
--- a/docs/tirx/arch/tile_dispatch.rst
+++ b/docs/tirx/arch/tile_dispatch.rst
@@ -28,8 +28,9 @@ The public C++ tile interfaces are grouped under
``include/tvm/tirx/tile/``:
- ``op.h`` declares the instruction registration and validation contract.
- ``dispatch.h`` declares ``FTileOpDispatch`` and the dispatcher entry point.
-- ``exec_scope.h`` defines backend execution hierarchy nodes used by layouts
- and tile calls. The shared ``tirx::ExecScope`` type remains unchanged.
+- ``thread_scope.h`` defines backend-qualified ``tirx::ThreadScope`` enum tags
+ used by layouts, tile Calls, and dispatch contexts. Backends attach explicit
+ hierarchy ranks through enum attributes.
- ``context.h`` defines the backend-neutral ``tile::DispatchContext`` and its
allocation and initialization callbacks.
@@ -41,7 +42,8 @@ active-thread execution contexts, and predicate analysis
remain private to
``tirx.TileDispatch`` remains the first phase of ``LowerTIRx``.
The pass recognizes ``Evaluate(Call)`` by the presence of the operator's
``FTileOpDispatch`` attribute. It validates the call, reads its reflected
-``scope`` attribute, resolves the scope against the active thread set,
+``ThreadScope`` ``scope`` attribute, checks its backend against the target,
+resolves the scope against the active thread set,
constructs a ``DispatchContext``, and
invokes the instruction's registered lowerer. It replaces the Evaluate with
the returned function body and lowers any nested tensor calls.
diff --git a/include/tvm/tirx/layout.h b/include/tvm/tirx/layout.h
index ea8bb5bd7f..e499fa38e4 100644
--- a/include/tvm/tirx/layout.h
+++ b/include/tvm/tirx/layout.h
@@ -31,7 +31,7 @@
#include <tvm/ir/expr.h>
#include <tvm/ir/module.h>
#include <tvm/target/target.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
namespace tvm {
@@ -78,7 +78,7 @@ class Layout : public ffi::ObjectRef {
};
// target, subscope, scope, iter -> fused_iter
-using FAxisFuser = ffi::TypedFunction<ffi::Optional<Iter>(Target, ffi::String,
ffi::String, Iter)>;
+using FAxisFuser = ffi::TypedFunction<ffi::Optional<Iter>(Target, ThreadScope,
ThreadScope, Iter)>;
// Axis
class AxisNode : public ffi::EnumObj {
@@ -93,10 +93,10 @@ class AxisNode : public ffi::EnumObj {
ffi::Optional<ffi::String> GetBackend() const;
/*! \brief Get the scope of the (thread) axis. */
- ffi::Optional<ExecScope> GetScope() const;
+ ffi::Optional<ThreadScope> GetScope() const;
/*! \brief Get the subscope of the (thread) axis. */
- ffi::Optional<ExecScope> GetSubscope() const;
+ ffi::Optional<ThreadScope> GetSubscope() const;
/*! \brief Get the fuser of the (thread) axis. */
ffi::Optional<FAxisFuser> GetFuser() const;
@@ -198,7 +198,7 @@ class TileLayoutNode : public LayoutNode {
bool HasThreadAxis() const;
/*! \brief Get the scope pair of the layout */
- ffi::Optional<Tuple<ExecScope, ExecScope>> GetScope() const;
+ ffi::Optional<Tuple<ThreadScope, ThreadScope>> GetScope() const;
/*! \brief Get the default layout for the shape */
TVM_TIRX_DLL static TileLayout DefaultLayout(ffi::Array<PrimExpr> shape);
diff --git a/include/tvm/tirx/script/ir_builder/frame.h
b/include/tvm/tirx/script/ir_builder/frame.h
index c8c9c0d901..04e8cbc228 100644
--- a/include/tvm/tirx/script/ir_builder/frame.h
+++ b/include/tvm/tirx/script/ir_builder/frame.h
@@ -23,7 +23,7 @@
#include <tvm/script/ir_builder/frame.h>
#include <tvm/tirx/function.h>
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <functional>
#include <utility>
diff --git a/include/tvm/tirx/script/ir_builder/ir.h
b/include/tvm/tirx/script/ir_builder/ir.h
index e9b4a61e62..4c47bf960b 100644
--- a/include/tvm/tirx/script/ir_builder/ir.h
+++ b/include/tvm/tirx/script/ir_builder/ir.h
@@ -26,7 +26,7 @@
#include <tvm/script/ir_builder/ir.h>
#include <tvm/tirx/script/ir_builder/frame.h>
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <tvm/tirx/type.h>
namespace tvm {
@@ -52,8 +52,8 @@ using tvm::Var;
using tvm::ffi::Tensor;
using tvm::ffi::Tuple;
using tvm::ffi::Variant;
-using tvm::tirx::ExecScope;
using tvm::tirx::TensorVar;
+using tvm::tirx::ThreadScope;
/*!
* \brief The primitive function statement.
diff --git a/include/tvm/tirx/stmt.h b/include/tvm/tirx/stmt.h
index 95cb330ae7..eb1b26a4a7 100644
--- a/include/tvm/tirx/stmt.h
+++ b/include/tvm/tirx/stmt.h
@@ -24,7 +24,7 @@
#include <tvm/ir/stmt.h>
#include <tvm/tirx/expr.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
namespace tvm {
namespace tirx {
diff --git a/include/tvm/tirx/tile/context.h b/include/tvm/tirx/tile/context.h
index 23591e400a..b075f35d1c 100644
--- a/include/tvm/tirx/tile/context.h
+++ b/include/tvm/tirx/tile/context.h
@@ -22,7 +22,7 @@
#include <tvm/target/target.h>
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
namespace tvm {
namespace tirx {
@@ -50,15 +50,15 @@ constexpr const char* kPostTensorDefStmt =
"post_tensor_def_stmt";
*/
class DispatchContextObj : public ffi::Object {
public:
- DispatchContextObj(Target target, ExecScope exec_scope)
- : target(std::move(target)), exec_scope(std::move(exec_scope)) {}
+ DispatchContextObj(Target target, ThreadScope thread_scope)
+ : target(std::move(target)), thread_scope(std::move(thread_scope)) {}
explicit DispatchContextObj(ffi::UnsafeInit)
- : target(ffi::UnsafeInit{}), exec_scope(ffi::UnsafeInit{}) {}
+ : target(ffi::UnsafeInit{}), thread_scope(ffi::UnsafeInit{}) {}
/*! \brief The target of the kernel. */
Target target;
- /*! \brief The exec scope of the operator */
- ExecScope exec_scope;
+ /*! \brief The thread scope of the operator */
+ ThreadScope thread_scope;
/*! \brief Hardware tag to (index variable, extent) launch parameters. */
ffi::Map<ffi::String, ffi::Tuple<PrimVar, PrimExpr>> launch_params;
/*! \brief A map from loop variables to their ranges. */
@@ -75,7 +75,7 @@ class DispatchContextObj : public ffi::Object {
* Maps axis name ("laneid"/"warpid"/"cta_id"/"wid_in_wg"/"wgid") to a
* 2-element [extent, offset] PrimExpr array. Empty map = no ExecContext
* tracking available (fallback for unresolved filters, pre-Phase-4 call
- * sites, etc.); dispatchers should fall back to exec_scope.name in that
+ * sites, etc.); dispatchers should fall back to thread_scope.local_name in
that
* case.
*/
ffi::Map<ffi::String, ffi::Array<PrimExpr>> inter;
@@ -88,7 +88,7 @@ class DispatchContextObj : public ffi::Object {
namespace refl = tvm::ffi::reflection;
refl::ObjectDef<DispatchContextObj>()
.def_ro("target", &DispatchContextObj::target)
- .def_ro("exec_scope", &DispatchContextObj::exec_scope)
+ .def_ro("thread_scope", &DispatchContextObj::thread_scope)
.def_ro("launch_params", &DispatchContextObj::launch_params)
.def_ro("var_range_map", &DispatchContextObj::var_range_map)
.def_ro("alloc_only", &DispatchContextObj::alloc_only)
@@ -124,7 +124,7 @@ class DispatchContextObj : public ffi::Object {
class DispatchContext : public ffi::ObjectRef {
public:
TVM_TIRX_DLL DispatchContext(
- Target target, ExecScope exec_scope,
+ Target target, ThreadScope thread_scope,
ffi::Map<ffi::String, ffi::Tuple<PrimVar, PrimExpr>> launch_params = {},
ffi::Map<Var, Range> var_range_map = {}, bool alloc_only = false,
ffi::Map<ffi::String, ffi::ObjectRef> callbacks = {},
diff --git a/include/tvm/tirx/tile/exec_scope.h
b/include/tvm/tirx/tile/exec_scope.h
deleted file mode 100644
index 541f6391cc..0000000000
--- a/include/tvm/tirx/tile/exec_scope.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-/*! \brief Backend-defined execution hierarchy nodes shared by layouts and
tile calls. */
-#ifndef TVM_TIRX_TILE_EXEC_SCOPE_H_
-#define TVM_TIRX_TILE_EXEC_SCOPE_H_
-#include <tvm/ffi/object.h>
-#include <tvm/ir/expr.h>
-
-namespace tvm::tirx {
-
-/*! \brief Register a backend's execution hierarchy. Smaller rank means wider
scope. */
-TVM_TIRX_DLL void RegisterExecScope(const ffi::String& backend, const
ffi::String& name, int rank);
-
-class ExecScopeNode : public ffi::Object {
- public:
- ffi::String backend;
- ffi::String scope_name;
- ffi::String name() const { return scope_name; }
- int rank() const;
-
- static void RegisterReflection() {
- namespace refl = tvm::ffi::reflection;
- refl::ObjectDef<ExecScopeNode>()
- .def_ro("backend", &ExecScopeNode::backend)
- .def_ro("name", &ExecScopeNode::scope_name);
- }
- static constexpr TVMFFISEqHashKind _type_s_eq_hash_kind =
kTVMFFISEqHashKindTreeNode;
- TVM_FFI_DECLARE_OBJECT_INFO("tirx.ExecScope", ExecScopeNode, ffi::Object);
-};
-
-class ExecScope : public ffi::ObjectRef {
- public:
- TVM_TIRX_DLL ExecScope(ffi::String backend, ffi::String name);
- TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(ExecScope, ffi::ObjectRef,
ExecScopeNode);
-};
-
-/*! \brief Compare scopes within the same backend's hierarchy. */
-TVM_TIRX_DLL bool ScopeHigher(const ExecScope& a, const ExecScope& b);
-
-} // namespace tvm::tirx
-#endif // TVM_TIRX_TILE_EXEC_SCOPE_H_
diff --git a/include/tvm/tirx/tile/thread_scope.h
b/include/tvm/tirx/tile/thread_scope.h
new file mode 100644
index 0000000000..98957211d8
--- /dev/null
+++ b/include/tvm/tirx/tile/thread_scope.h
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*! \brief Enum identities for backend-defined thread hierarchies. */
+#ifndef TVM_TIRX_TILE_THREAD_SCOPE_H_
+#define TVM_TIRX_TILE_THREAD_SCOPE_H_
+#include <tvm/ffi/enum.h>
+#include <tvm/ffi/reflection/enum_def.h>
+#include <tvm/runtime/base.h>
+
+#include <ostream>
+
+namespace tvm::tirx {
+
+namespace thread_scope_attr {
+/*! \brief Backend owning a thread scope. */
+inline constexpr ffi::reflection::AttrKey<ffi::String> kBackend{"backend"};
+/*! \brief Explicit hierarchy rank. Smaller ranks denote wider scopes. */
+inline constexpr ffi::reflection::AttrKey<int64_t> kRank{"rank"};
+} // namespace thread_scope_attr
+
+class ThreadScopeObj : public ffi::EnumObj {
+ public:
+ const ffi::String& name() const { return _str_index; }
+ TVM_TIRX_DLL ffi::String backend() const;
+ TVM_TIRX_DLL ffi::String local_name() const;
+ TVM_TIRX_DLL int64_t rank() const;
+ TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.ThreadScope", ThreadScopeObj,
ffi::EnumObj);
+};
+
+/*!
+ * \brief Canonical thread-scope identity, named as backend.scope.
+ *
+ * Backends register tags with EnumDef<ThreadScopeObj> and explicit backend and
+ * rank attributes. Registration order has no hierarchy semantics.
+ */
+class ThreadScope : public ffi::Enum {
+ public:
+ explicit ThreadScope(const ffi::String& name) : ffi::Enum(Get(name)) {}
+ TVM_TIRX_DLL static ThreadScope Get(const ffi::String& name);
+ const ffi::String& name() const { return get()->name(); }
+ bool operator==(const ThreadScope& other) const { return same_as(other); }
+ bool operator!=(const ThreadScope& other) const { return !same_as(other); }
+ TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE(ThreadScope, ffi::Enum,
ThreadScopeObj);
+};
+
+/*! \brief Compare explicit ranks within one backend's hierarchy. */
+TVM_TIRX_DLL bool ScopeHigher(const ThreadScope& a, const ThreadScope& b);
+
+inline std::ostream& operator<<(std::ostream& os, const ThreadScope& scope) {
+ return os << scope.name();
+}
+
+} // namespace tvm::tirx
+
+namespace tvm::ffi {
+template <>
+inline constexpr bool use_default_type_traits_v<tirx::ThreadScope> = false;
+template <>
+struct TypeTraits<tirx::ThreadScope>
+ : public ObjectRefWithFallbackTraitsBase<tirx::ThreadScope, ffi::String> {
+ static tirx::ThreadScope ConvertFallbackValue(ffi::String name) {
+ return tirx::ThreadScope::Get(name);
+ }
+};
+} // namespace tvm::ffi
+#endif // TVM_TIRX_TILE_THREAD_SCOPE_H_
diff --git a/python/tvm/backend/cuda/__init__.py
b/python/tvm/backend/cuda/__init__.py
index 98ad35d5a9..44d71992a8 100644
--- a/python/tvm/backend/cuda/__init__.py
+++ b/python/tvm/backend/cuda/__init__.py
@@ -33,7 +33,7 @@ _LAZY_SUBMODULES = {
"lang",
"layout",
"axis",
- "exec_scope",
+ "thread_scope",
"launch",
"op",
"ptx",
@@ -60,9 +60,8 @@ def _detect_target_from_device(dev):
def register_backend():
"""Register CUDA-owned Python semantics."""
- from . import axis, exec_scope
+ from . import axis
- exec_scope.register()
axis.register()
from tvm.target.detect_target import register_device_target_detector
from tvm.tirx.script.ir_builder import (
@@ -127,7 +126,6 @@ __all__ = [
"axis",
"codegen",
"cpp",
- "exec_scope",
"export_cuda_host",
"iket",
"lang",
@@ -140,6 +138,7 @@ __all__ = [
"script_namespace",
"script_namespaces",
"target_tags",
+ "thread_scope",
"tile",
"transforms",
]
diff --git a/python/tvm/backend/cuda/script.py
b/python/tvm/backend/cuda/script.py
index 4b6cdedec8..9db0ef5e2b 100644
--- a/python/tvm/backend/cuda/script.py
+++ b/python/tvm/backend/cuda/script.py
@@ -25,7 +25,6 @@ from tvm.script.ir_builder.base import auto_emit_void_call as
_auto_emit_void_ca
from . import layout as layout
from .axis import Axis as Axis
from .backend_config import BackendConfig as BackendConfig
-from .exec_scope import ExecScope as ExecScope
from .launch import AccessPolicyWindow as AccessPolicyWindow
from .launch import KernelAttributes as KernelAttributes
from .launch import LaunchCompletionEvent as LaunchCompletionEvent
@@ -34,6 +33,7 @@ from .launch import MemSyncDomainMap as MemSyncDomainMap
from .launch import ProgrammaticEvent as ProgrammaticEvent
from .ptx.engine import PTXNamespace as _PTXNamespace
from .ptx.engine import _InstrChain
+from .thread_scope import ThreadScope as ThreadScope
from .tile.instructions import make_namespace as _make_tensor_namespace
# pylint: disable=protected-access
diff --git a/python/tvm/backend/trn/exec_scope.py
b/python/tvm/backend/cuda/thread_scope.py
similarity index 67%
rename from python/tvm/backend/trn/exec_scope.py
rename to python/tvm/backend/cuda/thread_scope.py
index 7c69780d5f..2103fa1c28 100644
--- a/python/tvm/backend/trn/exec_scope.py
+++ b/python/tvm/backend/cuda/thread_scope.py
@@ -16,19 +16,11 @@
# under the License.
# pylint: disable=no-member, super-init-not-called
-"""TRN execution hierarchy."""
+"""CUDA thread-scope lookup."""
-from tvm.tirx.exec_scope import ExecScope as _ExecScope
-from tvm.tirx.exec_scope import register_exec_scope
+from tvm.tirx.thread_scope import ThreadScope as _ThreadScope
-SCOPES = ("kernel", "thread")
-
-def register():
- for rank, name in enumerate(SCOPES, start=-1):
- register_exec_scope("trn", name, rank)
-
-
-def ExecScope(name: str) -> _ExecScope:
- """Construct a TRN execution scope."""
- return _ExecScope("trn", name)
+def ThreadScope(name: str) -> _ThreadScope:
+ """Look up a CUDA thread scope by its backend-local name."""
+ return _ThreadScope("cuda." + name)
diff --git a/python/tvm/backend/trn/__init__.py
b/python/tvm/backend/trn/__init__.py
index 12e9d01bcf..e47553953e 100644
--- a/python/tvm/backend/trn/__init__.py
+++ b/python/tvm/backend/trn/__init__.py
@@ -21,7 +21,7 @@ from importlib import import_module
_LAZY_SUBMODULES = {
"layout",
"axis",
- "exec_scope",
+ "thread_scope",
"op",
"pipeline",
"script",
@@ -33,9 +33,8 @@ _LAZY_SUBMODULES = {
def register_backend():
"""Register Trainium-owned Python semantics."""
- from . import axis, exec_scope
+ from . import axis
- exec_scope.register()
axis.register()
from tvm.tirx import compilation_pipeline # pylint:
disable=import-outside-toplevel
from tvm.tirx.script.ir_builder import (
@@ -65,14 +64,14 @@ def script_namespaces(**_):
from . import layout
from .axis import Axis
- from .exec_scope import ExecScope
+ from .thread_scope import ThreadScope
from .tile import private_alloc # register workspace policies
from .tile.instructions import make_namespace
return {
"nki": script_namespace(),
"trn": SimpleNamespace(
- tile=make_namespace(), Axis=Axis, ExecScope=ExecScope,
layout=layout
+ tile=make_namespace(), Axis=Axis, ThreadScope=ThreadScope,
layout=layout
),
}
@@ -85,7 +84,6 @@ def __getattr__(name: str):
__all__ = [
"axis",
- "exec_scope",
"layout",
"op",
"pipeline",
@@ -94,6 +92,7 @@ __all__ = [
"script_namespace",
"script_namespaces",
"target_tags",
+ "thread_scope",
"tile",
"transform",
]
diff --git a/python/tvm/backend/cuda/exec_scope.py
b/python/tvm/backend/trn/thread_scope.py
similarity index 65%
rename from python/tvm/backend/cuda/exec_scope.py
rename to python/tvm/backend/trn/thread_scope.py
index e2cbd2d15b..9c35e31c0d 100644
--- a/python/tvm/backend/cuda/exec_scope.py
+++ b/python/tvm/backend/trn/thread_scope.py
@@ -16,19 +16,11 @@
# under the License.
# pylint: disable=no-member, super-init-not-called
-"""CUDA execution hierarchy."""
+"""TRN thread-scope lookup."""
-from tvm.tirx.exec_scope import ExecScope as _ExecScope
-from tvm.tirx.exec_scope import register_exec_scope
+from tvm.tirx.thread_scope import ThreadScope as _ThreadScope
-SCOPES = ("kernel", "cluster", "cta", "warpgroup", "warp", "thread")
-
-def register():
- for rank, name in enumerate(SCOPES, start=-1):
- register_exec_scope("cuda", name, rank)
-
-
-def ExecScope(name: str) -> _ExecScope:
- """Construct a CUDA execution scope."""
- return _ExecScope("cuda", name)
+def ThreadScope(name: str) -> _ThreadScope:
+ """Look up a TRN thread scope by its backend-local name."""
+ return _ThreadScope("trn." + name)
diff --git a/python/tvm/backend/trn/tile/binary/default.py
b/python/tvm/backend/trn/tile/binary/default.py
index 6d3ee356ce..ff3ad80174 100644
--- a/python/tvm/backend/trn/tile/binary/default.py
+++ b/python/tvm/backend/trn/tile/binary/default.py
@@ -31,7 +31,7 @@ from .utils import InstType, binary_map_ops,
try_find_inst_nary
def binary_trn(op: TensorCall, binary_op: MapOpType, sctx: DispatchContext) ->
Function | None:
"""Generate a binary operation schedule for Trainium."""
if not (sctx.is_target("trn") and sctx.scope_kind == "thread"):
- fail("requires Trainium target and thread exec_scope")
+ fail("requires Trainium target and thread scope")
assert binary_op in binary_map_ops, f"Unsupported binary operation
{binary_op}"
diff --git a/python/tvm/backend/trn/tile/copy/default.py
b/python/tvm/backend/trn/tile/copy/default.py
index 5660a4f6b4..b3d9dffe10 100644
--- a/python/tvm/backend/trn/tile/copy/default.py
+++ b/python/tvm/backend/trn/tile/copy/default.py
@@ -47,7 +47,7 @@ def copy_trn(op: TensorCall, sctx: DispatchContext) ->
Function | None:
"""Schedule copy operation between global and shared memory on CUDA."""
# Basic validation checks
if sctx.scope_kind != "thread":
- fail("requires thread exec_scope for TRN copy")
+ fail("requires thread scope for TRN copy")
dst_region, src_region = op.args
src, dst = src_region.source, dst_region.source
diff --git a/python/tvm/backend/trn/tile/gemm/default.py
b/python/tvm/backend/trn/tile/gemm/default.py
index e20add61b7..da8efff59f 100644
--- a/python/tvm/backend/trn/tile/gemm/default.py
+++ b/python/tvm/backend/trn/tile/gemm/default.py
@@ -121,7 +121,7 @@ def matmul_trn(op: TensorCall, sctx: DispatchContext) ->
Function | None:
"""Schedule GEMM operation on Trainium."""
# Basic validation checks
if not (sctx.is_target("trn") and sctx.scope_kind == "thread"):
- fail("requires Trainium target and thread exec_scope")
+ fail("requires Trainium target and thread scope")
# Extract arguments
(
diff --git a/python/tvm/backend/trn/tile/reduction/utils.py
b/python/tvm/backend/trn/tile/reduction/utils.py
index 5e64bceff5..e4c6732f30 100644
--- a/python/tvm/backend/trn/tile/reduction/utils.py
+++ b/python/tvm/backend/trn/tile/reduction/utils.py
@@ -87,7 +87,7 @@ def reduction_trn(
Optional[Function]: The scheduled function, or None if not applicable.
"""
if not (sctx.is_target("trn") and sctx.scope_kind == "thread"):
- fail("requires Trainium target and thread exec_scope")
+ fail("requires Trainium target and thread scope")
op = TensorCall.decode(op)
dst_tensor_region, src_tensor_region = op.output, op.input
diff --git a/python/tvm/backend/trn/tile/select/default.py
b/python/tvm/backend/trn/tile/select/default.py
index f6506ac510..c591a51c14 100644
--- a/python/tvm/backend/trn/tile/select/default.py
+++ b/python/tvm/backend/trn/tile/select/default.py
@@ -41,7 +41,7 @@ def _scope_trn_sbuf():
def select_trn(op: TensorCall, sctx: DispatchContext) -> Function | None:
"""Generate schedule for select operation on Trainium."""
if sctx.scope_kind != "thread":
- fail("requires thread exec_scope for TRN select")
+ fail("requires thread scope for TRN select")
op = TensorCall.decode(op)
assert isinstance(op, Select), f"{op} is not a Select"
diff --git a/python/tvm/backend/trn/tile/unary/default.py
b/python/tvm/backend/trn/tile/unary/default.py
index 4b34297e4a..1cbaae9fb8 100644
--- a/python/tvm/backend/trn/tile/unary/default.py
+++ b/python/tvm/backend/trn/tile/unary/default.py
@@ -36,7 +36,7 @@ def unary_trn(op: TensorCall, unary_op: MapOpType, sctx:
DispatchContext) -> Fun
"""Schedule unary operation on Trainium."""
# Check execution environment
if not (sctx.is_target("trn") and sctx.scope_kind == "thread"):
- fail("requires Trainium target and thread exec_scope")
+ fail("requires Trainium target and thread scope")
# Extract operation arguments
dst_tensor_region, _src = op.args
diff --git a/python/tvm/backend/trn/tile/unary/with_bias_scale.py
b/python/tvm/backend/trn/tile/unary/with_bias_scale.py
index 1e1e08a8c9..149f96ec64 100644
--- a/python/tvm/backend/trn/tile/unary/with_bias_scale.py
+++ b/python/tvm/backend/trn/tile/unary/with_bias_scale.py
@@ -35,7 +35,7 @@ def unary_with_bias_scale_trn(
"""Schedule unary operation with bias and scale on Trainium."""
# Check execution environment
if not (sctx.is_target("trn") and sctx.scope_kind == "thread"):
- fail("requires Trainium target and thread exec_scope")
+ fail("requires Trainium target and thread scope")
if len(op.args) == 2:
dst_tensor_region, src_tensor_region = op.args
diff --git a/python/tvm/backend/trn/transform/private_tensor_alloc.py
b/python/tvm/backend/trn/transform/private_tensor_alloc.py
index 6fbab19b3e..11ec7c486b 100644
--- a/python/tvm/backend/trn/transform/private_tensor_alloc.py
+++ b/python/tvm/backend/trn/transform/private_tensor_alloc.py
@@ -61,11 +61,11 @@ def _collect_private_allocations(stmt: Stmt, target:
Target):
return
call = op
op = TensorCall.decode(call)
- exec_scope = op.scope
- scope_kind = op.scope.name
+ thread_scope = op.scope
+ scope_kind = op.scope.local_name
sctx = DispatchContext(
target=target,
- exec_scope=exec_scope,
+ thread_scope=thread_scope,
launch_params=launch_params,
var_range_map=var_range_map,
alloc_only=True,
diff --git a/python/tvm/tirx/__init__.py b/python/tvm/tirx/__init__.py
index 5c605f686d..b79baf8c63 100644
--- a/python/tvm/tirx/__init__.py
+++ b/python/tvm/tirx/__init__.py
@@ -78,7 +78,7 @@ from .op import gpu_dp4a
from .op import ignore_loop_partition
# TIRX-specific imports (must come before subpackage imports to avoid circular
imports)
-from .exec_scope import ExecScope
+from .thread_scope import ThreadScope
from .layout import TileLayout, Layout, ComposeLayout
from . import transform
from . import analysis
diff --git a/python/tvm/tirx/exec_scope.py b/python/tvm/tirx/exec_scope.py
deleted file mode 100644
index fecd51b539..0000000000
--- a/python/tvm/tirx/exec_scope.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-# pylint: disable=no-member, super-init-not-called
-
-"""Backend-defined execution scope identities."""
-
-from tvm_ffi import register_object
-
-from tvm.ir import Scriptable
-from tvm.runtime import Object
-
-from . import _ffi_api
-
-
-@register_object("tirx.ExecScope")
-class ExecScope(Object, Scriptable):
- """A node in one backend's registered execution hierarchy."""
-
- backend: str
- name: str
-
- def __init__(self, backend: str, name: str):
- self.__init_handle_by_constructor__(_ffi_api.ExecScope, backend, name)
-
-
-def register_exec_scope(backend: str, name: str, rank: int):
- """Register a backend's finite scope hierarchy, from outer to inner ranks.
-
- Backend load hooks register scopes before registering their layout axes.
- """
- _ffi_api.RegisterExecScope(backend, name, rank)
diff --git a/python/tvm/tirx/layout.py b/python/tvm/tirx/layout.py
index e43954aeae..e7056d0055 100644
--- a/python/tvm/tirx/layout.py
+++ b/python/tvm/tirx/layout.py
@@ -28,7 +28,7 @@ from tvm.runtime import Object
from tvm.tirx.expr import Expr
from . import _ffi_api
-from .exec_scope import ExecScope
+from .thread_scope import ThreadScope
@tvm_ffi.register_object("tirx.Layout")
@@ -312,11 +312,11 @@ class Axis(Enum, metaclass=_AxisMeta,
type_key="tirx.Axis", init=False):
"""Backend owning this axis, or None for a generic axis."""
return _ffi_api.AxisGetBackend(self)
- def get_scope(self) -> ExecScope | None:
+ def get_scope(self) -> ThreadScope | None:
"""Get the scope of the axis."""
return _ffi_api.AxisGetScope(self) # pylint: disable=no-member
- def get_subscope(self) -> ExecScope | None:
+ def get_subscope(self) -> ThreadScope | None:
"""Get the subscope of the axis."""
return _ffi_api.AxisGetSubscope(self) # pylint: disable=no-member
@@ -597,7 +597,7 @@ class TileLayout(Layout):
"""
return _ffi_api.TileLayoutGroup(self, shape) # pylint:
disable=no-member
- def get_scope(self) -> tuple[ExecScope, ExecScope] | None:
+ def get_scope(self) -> tuple[ThreadScope, ThreadScope] | None:
"""Get the scope pair of the layout."""
return _ffi_api.TileLayoutGetScope(self) # pylint: disable=no-member
diff --git a/python/tvm/tirx/script/ir_builder/ir.py
b/python/tvm/tirx/script/ir_builder/ir.py
index d4ffb16a95..da4d9b9615 100644
--- a/python/tvm/tirx/script/ir_builder/ir.py
+++ b/python/tvm/tirx/script/ir_builder/ir.py
@@ -42,7 +42,6 @@ from tvm.target import Target
# pylint: disable=unused-import
from tvm.tirx import Expr, IndexMap, Var, is_tensor_var, tile
-from tvm.tirx.exec_scope import ExecScope
# import tirx.expr for direct ir construction to pass structural_equal
comparison
from tvm.tirx.expr import (
@@ -51,6 +50,7 @@ from tvm.tirx.expr import (
TensorLoad,
)
from tvm.tirx.layout import Axis, ComposeLayout, Iter, Layout, R, S, TileLayout
+from tvm.tirx.thread_scope import ThreadScope
from . import _ffi_api
@@ -1064,7 +1064,6 @@ Tensor = tir.TensorType
__all__ = [
"Axis",
"ComposeLayout",
- "ExecScope",
"FloatImm",
"IntImm",
"Iter",
@@ -1080,6 +1079,7 @@ __all__ = [
"TensorLoad",
"TensorMap",
"TensorRegion",
+ "ThreadScope",
"TileLayout",
"Tuple",
"Var",
diff --git a/python/tvm/tirx/thread_scope.py b/python/tvm/tirx/thread_scope.py
new file mode 100644
index 0000000000..df99a6f30e
--- /dev/null
+++ b/python/tvm/tirx/thread_scope.py
@@ -0,0 +1,64 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# pylint: disable=no-member, super-init-not-called
+
+"""Canonical enum tags for backend-defined thread hierarchies."""
+
+from tvm_ffi.dataclasses import Enum
+
+from tvm.ir import Scriptable
+
+from . import _ffi_api
+
+
+class ThreadScope(Enum, Scriptable, type_key="tirx.ThreadScope", init=False):
+ """A registered thread scope, queried by its qualified backend.scope name.
+
+ Backend and hierarchy rank are enum attributes. The registration index
+ does not determine the hierarchy.
+ """
+
+ @classmethod
+ def get(cls, name: str) -> "ThreadScope":
+ """Look up a registered tag without creating unknown scopes."""
+ return _ffi_api.ThreadScopeGet(name)
+
+ @property
+ def name(self) -> str:
+ """Canonical qualified name, such as cuda.warp."""
+ return self._str_index
+
+ @property
+ def backend(self) -> str:
+ """Backend owning this tag."""
+ return _ffi_api.ThreadScopeGetBackend(self)
+
+ @property
+ def local_name(self) -> str:
+ """Backend-local name, such as warp."""
+ return _ffi_api.ThreadScopeGetLocalName(self)
+
+ @property
+ def rank(self) -> int:
+ """Explicit hierarchy rank; smaller ranks denote wider scopes."""
+ return _ffi_api.ThreadScopeGetRank(self)
+
+ def __str__(self) -> str:
+ return self.name
+
+ def __repr__(self) -> str:
+ return f"ThreadScope({self.name!r})"
diff --git a/python/tvm/tirx/tile/dispatch.py b/python/tvm/tirx/tile/dispatch.py
index 12477fb3e8..8086040b12 100644
--- a/python/tvm/tirx/tile/dispatch.py
+++ b/python/tvm/tirx/tile/dispatch.py
@@ -27,7 +27,7 @@ from tvm.runtime import Object
from tvm.target import Target
from .. import _ffi_api
-from ..exec_scope import ExecScope
+from ..thread_scope import ThreadScope
@register_object("tirx.DispatchContext")
@@ -39,7 +39,7 @@ class DispatchContext(Object, Scriptable):
target : Target
The target of the dispatch context.
- exec_scope : ExecScope
+ thread_scope : ThreadScope
The execution scope of the dispatch context.
launch_params : Dict[str, Tuple[Var, Expr]]
@@ -56,7 +56,7 @@ class DispatchContext(Object, Scriptable):
"""
target: Target
- exec_scope: ExecScope
+ thread_scope: ThreadScope
launch_params: dict[str, tuple[Var, Expr]]
var_range_map: dict[Var, Range]
alloc_only: bool
@@ -74,7 +74,7 @@ class DispatchContext(Object, Scriptable):
def __init__(
self,
target: Target,
- exec_scope: ExecScope,
+ thread_scope: ThreadScope,
launch_params: dict[str, tuple[Var, Expr]],
var_range_map: dict[Var, Range],
alloc_only: bool = False,
@@ -87,7 +87,7 @@ class DispatchContext(Object, Scriptable):
self.__init_handle_by_constructor__(
_ffi_api.DispatchContext, # pylint: disable=no-member
target,
- exec_scope,
+ thread_scope,
launch_params,
var_range_map,
alloc_only,
diff --git a/python/tvm/tirx/tile/instruction.py
b/python/tvm/tirx/tile/instruction.py
index c24b358c6c..dd0ce83328 100644
--- a/python/tvm/tirx/tile/instruction.py
+++ b/python/tvm/tirx/tile/instruction.py
@@ -43,8 +43,8 @@ from tvm.ir import (
)
from tvm.ir.location import UNKNOWN_LOC
from tvm.ir.op import register_op_attr
-from tvm.tirx.exec_scope import ExecScope
from tvm.tirx.expr import IntImm
+from tvm.tirx.thread_scope import ThreadScope
_REQUIRED = object()
SPECS = {}
@@ -142,9 +142,6 @@ class Instruction:
unknown = set(kw) - set(allowed)
if unknown:
raise TypeError(f"{self.name}: unknown qualifier(s)
{sorted(unknown)}")
- scope = kw.get("scope")
- if isinstance(scope, ExecScope) and scope.backend != self.backend:
- raise ValueError(f"{self.name}: scope belongs to {scope.backend}")
attrs_values = {name: _static(value) for name, value in kw.items()}
if isinstance(attrs_values.get("axes"), int):
attrs_values["axes"] = [attrs_values["axes"]]
@@ -155,18 +152,34 @@ class Instruction:
raise TypeError("attrs cannot be combined with qualifier
keywords")
attrs = explicit_attrs
else:
+ attrs_values["scope"] = self._thread_scope(attrs_values["scope"])
attrs = make_node("tirx.tensor." + self.schema, **attrs_values)
call = Call(self.name, args, attrs=attrs, ty=result_ty, loc=loc)
call.validate()
return call
+ def _thread_scope(self, value):
+ if isinstance(value, str):
+ local_name = value.split(".", 1)[-1]
+ if local_name not in self.scopes:
+ raise ValueError(
+ f"{self.name}: unsupported scope {value!r}; expected
{self.scopes}"
+ )
+ value = ThreadScope(value if "." in value else
f"{self.backend}.{value}")
+ if not isinstance(value, ThreadScope):
+ raise TypeError(f"{self.name}: scope must be a string or
ThreadScope")
+ if value.backend != self.backend:
+ raise ValueError(f"{self.name}: scope belongs to {value.backend}")
+ if value.local_name not in self.scopes:
+ raise ValueError(
+ f"{self.name}: unsupported scope {value.local_name!r};
expected {self.scopes}"
+ )
+ return value
+
def validate(self, call):
if len(call.args) != len(self.operands):
raise TypeError(f"{self.name}: invalid operand count")
- if str(call.attrs.scope) not in self.scopes:
- raise ValueError(
- f"{self.name}: unsupported scope {call.attrs.scope!r};
expected {self.scopes}"
- )
+ self._thread_scope(call.attrs.scope)
for arg, value in zip(self.operands, call.args):
if _is_absent(value):
if arg.default is not None:
@@ -214,8 +227,8 @@ def _static(value):
return value.value
if isinstance(value, IntImm):
return bool(value) if str(value.dtype) == "bool" else int(value)
- if isinstance(value, ExecScope):
- return value.name
+ if isinstance(value, ThreadScope):
+ return value
if isinstance(value, Expr):
raise TypeError("Instruction qualifiers must be static; expressions
belong in operands")
return value
@@ -303,7 +316,7 @@ class TensorCall:
view.options = {k: _expr(v) for k, v in options.items() if v is not
None}
view.options.update({k: v for k, v in extras.items() if v is not None})
view.workspaces = {k: values[k] for k in spec.workspaces if values[k]
is not None}
- view.scope = ExecScope(spec.backend, str(call.attrs.scope))
+ view.scope = call.attrs.scope
return view
def get_private_tensors(self, tensor_dict, sctx):
diff --git a/src/backend/cuda/codegen/target_kind.cc
b/src/backend/cuda/codegen/target_kind.cc
index ac0732419f..5add58a024 100644
--- a/src/backend/cuda/codegen/target_kind.cc
+++ b/src/backend/cuda/codegen/target_kind.cc
@@ -31,6 +31,7 @@
#include <tvm/runtime/memory_scope.h>
#include <tvm/target/target.h>
#include <tvm/target/target_kind.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <string>
@@ -117,6 +118,17 @@ ffi::Map<ffi::String, ffi::Any>
UpdateNVPTXAttrs(ffi::Map<ffi::String, ffi::Any>
} // namespace tvm
TVM_FFI_STATIC_INIT_BLOCK() {
+ for (const auto& [name, rank] : {std::pair{"kernel", -1},
+ {"cluster", 0},
+ {"cta", 1},
+ {"warpgroup", 2},
+ {"warp", 3},
+ {"thread", 4}}) {
+ auto qualified = tvm::ffi::String("cuda.") + name;
+ tvm::ffi::reflection::EnumDef<tvm::tirx::ThreadScopeObj>(qualified.c_str())
+ .set_attr(tvm::tirx::thread_scope_attr::kBackend,
tvm::ffi::String("cuda"))
+ .set_attr(tvm::tirx::thread_scope_attr::kRank, rank);
+ }
for (const char* name : {"tmem", "wmma.matrix_a", "wmma.matrix_b",
"wmma.accumulator",
"mma.matrixA", "mma.matrixB", "mma.matrixC"}) {
tvm::ffi::reflection::EnumDef<tvm::runtime::MemoryScopeObj>(name).set_attr(
diff --git a/src/backend/cuda/layout/axis.cc b/src/backend/cuda/layout/axis.cc
index 778d335cec..4e875a8ebd 100644
--- a/src/backend/cuda/layout/axis.cc
+++ b/src/backend/cuda/layout/axis.cc
@@ -30,45 +30,45 @@ void RegisterLayoutAxes() {
AxisDef("bx")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "kernel"))
- .set_attr("subscope", ExecScope("cuda", "cta"));
+ .set_attr("scope", ThreadScope("cuda.kernel"))
+ .set_attr("subscope", ThreadScope("cuda.cta"));
AxisDef("by")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "kernel"))
- .set_attr("subscope", ExecScope("cuda", "cta"));
+ .set_attr("scope", ThreadScope("cuda.kernel"))
+ .set_attr("subscope", ThreadScope("cuda.cta"));
AxisDef("bz")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "kernel"))
- .set_attr("subscope", ExecScope("cuda", "cta"));
+ .set_attr("scope", ThreadScope("cuda.kernel"))
+ .set_attr("subscope", ThreadScope("cuda.cta"));
AxisDef("cbx")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "cluster"))
- .set_attr("subscope", ExecScope("cuda", "cta"));
+ .set_attr("scope", ThreadScope("cuda.cluster"))
+ .set_attr("subscope", ThreadScope("cuda.cta"));
AxisDef("cby")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "cluster"))
- .set_attr("subscope", ExecScope("cuda", "cta"));
+ .set_attr("scope", ThreadScope("cuda.cluster"))
+ .set_attr("subscope", ThreadScope("cuda.cta"));
AxisDef("cbz")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "cluster"))
- .set_attr("subscope", ExecScope("cuda", "cta"));
+ .set_attr("scope", ThreadScope("cuda.cluster"))
+ .set_attr("subscope", ThreadScope("cuda.cta"));
AxisDef("tx")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "cta"))
- .set_attr("subscope", ExecScope("cuda", "thread"))
- .set_attr("fuser", FAxisFuser([](Target target, ffi::String subscope,
ffi::String scope,
+ .set_attr("scope", ThreadScope("cuda.cta"))
+ .set_attr("subscope", ThreadScope("cuda.thread"))
+ .set_attr("fuser", FAxisFuser([](Target target, ThreadScope subscope,
ThreadScope scope,
Iter iter) -> ffi::Optional<Iter> {
if (target->kind->default_device_type == kDLCUDA) {
return std::nullopt;
@@ -79,13 +79,14 @@ void RegisterLayoutAxes() {
AxisDef("warpid")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "cta"))
- .set_attr("subscope", ExecScope("cuda", "warp"))
- .set_attr("fuser", FAxisFuser([](Target target, ffi::String subscope,
ffi::String scope,
+ .set_attr("scope", ThreadScope("cuda.cta"))
+ .set_attr("subscope", ThreadScope("cuda.warp"))
+ .set_attr("fuser", FAxisFuser([](Target target, ThreadScope subscope,
ThreadScope scope,
Iter iter) -> ffi::Optional<Iter> {
if (target->kind->default_device_type == kDLCUDA) {
// cta->warp ===> cta->thread (tx)
- if (subscope == "thread" && scope == "cta") {
+ if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
return Iter(iter->extent, 32 * iter->stride,
Axis::Get("tx"));
}
return std::nullopt;
@@ -96,15 +97,17 @@ void RegisterLayoutAxes() {
AxisDef("laneid")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "warp"))
- .set_attr("subscope", ExecScope("cuda", "thread"))
- .set_attr("fuser", FAxisFuser([](Target target, ffi::String subscope,
ffi::String scope,
+ .set_attr("scope", ThreadScope("cuda.warp"))
+ .set_attr("subscope", ThreadScope("cuda.thread"))
+ .set_attr("fuser", FAxisFuser([](Target target, ThreadScope subscope,
ThreadScope scope,
Iter iter) -> ffi::Optional<Iter> {
if (target->kind->default_device_type == kDLCUDA) {
- if (subscope == "thread" && scope == "warpgroup") {
+ if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.warpgroup"))) {
// warp->thread ===> warpgroup->thread (tid_in_wg)
return Iter(iter->extent, iter->stride,
Axis::Get("tid_in_wg"));
- } else if (subscope == "thread" && scope == "cta") {
+ } else if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
// warp->thread ===> cta->thread (tx)
return Iter(iter->extent, iter->stride, Axis::Get("tx"));
}
@@ -116,15 +119,17 @@ void RegisterLayoutAxes() {
AxisDef("wgid")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "cta"))
- .set_attr("subscope", ExecScope("cuda", "warpgroup"))
- .set_attr("fuser", FAxisFuser([](Target target, ffi::String subscope,
ffi::String scope,
+ .set_attr("scope", ThreadScope("cuda.cta"))
+ .set_attr("subscope", ThreadScope("cuda.warpgroup"))
+ .set_attr("fuser", FAxisFuser([](Target target, ThreadScope subscope,
ThreadScope scope,
Iter iter) -> ffi::Optional<Iter> {
if (target->kind->default_device_type == kDLCUDA) {
- if (subscope == "thread" && scope == "cta") {
+ if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
// cta->warpgroup ===> cta->thread (tx)
return Iter(iter->extent, iter->stride * 128,
Axis::Get("tx"));
- } else if (subscope == "warp" && scope == "cta") {
+ } else if (subscope.same_as(ThreadScope("cuda.warp")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
// cta->warpgroup ===> cta->warp (warpid)
return Iter(iter->extent, iter->stride * 4,
Axis::Get("wgid"));
}
@@ -135,12 +140,13 @@ void RegisterLayoutAxes() {
AxisDef("tid_in_wg")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "warpgroup"))
- .set_attr("subscope", ExecScope("cuda", "thread"))
- .set_attr("fuser", FAxisFuser([](Target target, ffi::String subscope,
ffi::String scope,
+ .set_attr("scope", ThreadScope("cuda.warpgroup"))
+ .set_attr("subscope", ThreadScope("cuda.thread"))
+ .set_attr("fuser", FAxisFuser([](Target target, ThreadScope subscope,
ThreadScope scope,
Iter iter) -> ffi::Optional<Iter> {
if (target->kind->default_device_type == kDLCUDA) {
- if (subscope == "thread" && scope == "cta") {
+ if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
// warpgroup->thread ===> cta->thread (tx)
return Iter(iter->extent, iter->stride, Axis::Get("tx"));
}
@@ -152,18 +158,21 @@ void RegisterLayoutAxes() {
AxisDef("wid_in_wg")
.set_attr("backend", ffi::String("cuda"))
.set_attr("thread", true)
- .set_attr("scope", ExecScope("cuda", "warpgroup"))
- .set_attr("subscope", ExecScope("cuda", "warp"))
- .set_attr("fuser", FAxisFuser([](Target target, ffi::String subscope,
ffi::String scope,
+ .set_attr("scope", ThreadScope("cuda.warpgroup"))
+ .set_attr("subscope", ThreadScope("cuda.warp"))
+ .set_attr("fuser", FAxisFuser([](Target target, ThreadScope subscope,
ThreadScope scope,
Iter iter) -> ffi::Optional<Iter> {
if (target->kind->default_device_type == kDLCUDA) {
- if (subscope == "thread" && scope == "warpgroup") {
+ if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.warpgroup"))) {
// warpgroup->warp ===> warpgroup->thread (tid_in_wg)
return Iter(iter->extent, iter->stride * 32,
Axis::Get("tid_in_wg"));
- } else if (subscope == "thread" && scope == "cta") {
+ } else if (subscope.same_as(ThreadScope("cuda.thread")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
// warpgroup->warp ===> cta->thread (tx)
return Iter(iter->extent, iter->stride * 32,
Axis::Get("tx"));
- } else if (subscope == "warp" && scope == "cta") {
+ } else if (subscope.same_as(ThreadScope("cuda.warp")) &&
+ scope.same_as(ThreadScope("cuda.cta"))) {
// warpgroup->warp ===> cta->warp (warpid)
return Iter(iter->extent, iter->stride,
Axis::Get("warpid"));
}
diff --git a/src/backend/cuda/tile/exec_analysis.cc
b/src/backend/cuda/tile/exec_analysis.cc
index 7616bb1b65..2368586a40 100644
--- a/src/backend/cuda/tile/exec_analysis.cc
+++ b/src/backend/cuda/tile/exec_analysis.cc
@@ -36,7 +36,7 @@
#include <tvm/tirx/stmt.h>
#include <tvm/tirx/stmt_functor.h>
#include <tvm/tirx/tile/context.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <tvm/tirx/transform.h>
#include <algorithm>
@@ -1039,14 +1039,16 @@ void ExecAnalysis::Pop(int count) {
PrimExpr ExecAnalysis::RewritePredicate(const PrimExpr& condition) const {
return impl_->RewriteFilterCalls(condition);
}
-std::pair<SplitMap, SplitMap> ExecAnalysis::Split(const tirx::ExecScope&
scope) const {
- TVM_FFI_CHECK(!impl_->variable_cluster_ || scope->name() != "cluster",
ValueError)
+std::pair<SplitMap, SplitMap> ExecAnalysis::Split(const tirx::ThreadScope&
scope) const {
+ TVM_FFI_CHECK(scope->backend() == "cuda", ValueError) << "Expected a CUDA
thread scope";
+ TVM_FFI_CHECK(!impl_->variable_cluster_ || scope->local_name() != "cluster",
ValueError)
<< "Cluster tensor instructions require one static cluster shape; "
"use explicit CUDA/PTX instructions for variable or preferred cluster
shapes";
if (impl_->ctx_stack_.empty()) return {};
ExecSplit split;
std::string err;
- if (ScopeSwitch(impl_->ctx_stack_.back().A,
StringToScopeKind(scope->name()), &split, &err)) {
+ if (ScopeSwitch(impl_->ctx_stack_.back().A,
StringToScopeKind(scope->local_name()), &split,
+ &err)) {
return {EncodeSplitSide(split.inter), EncodeSplitSide(split.intra)};
}
LOG(WARNING) << "ExecContext scope_switch failed: " << err;
diff --git a/src/backend/cuda/tile/exec_analysis.h
b/src/backend/cuda/tile/exec_analysis.h
index f744871751..18f193d424 100644
--- a/src/backend/cuda/tile/exec_analysis.h
+++ b/src/backend/cuda/tile/exec_analysis.h
@@ -20,7 +20,7 @@
#ifndef TVM_BACKEND_CUDA_TILE_EXEC_ANALYSIS_H_
#define TVM_BACKEND_CUDA_TILE_EXEC_ANALYSIS_H_
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <memory>
#include <string>
@@ -38,7 +38,7 @@ class ExecAnalysis {
int PushPredicate(const PrimExpr& condition);
void Pop(int count);
PrimExpr RewritePredicate(const PrimExpr& condition) const;
- std::pair<SplitMap, SplitMap> Split(const tirx::ExecScope& scope) const;
+ std::pair<SplitMap, SplitMap> Split(const tirx::ThreadScope& scope) const;
private:
struct Impl;
diff --git a/src/backend/cuda/tile/exec_context.h
b/src/backend/cuda/tile/exec_context.h
index 31585d1f4b..4a02d1782f 100644
--- a/src/backend/cuda/tile/exec_context.h
+++ b/src/backend/cuda/tile/exec_context.h
@@ -35,7 +35,7 @@
#include <unordered_map>
#include <vector>
-#include "exec_scope.h"
+#include "thread_scope.h"
namespace tvm::backend::cuda {
using namespace tvm::tirx;
diff --git a/src/backend/cuda/tile/exec_scope.cc
b/src/backend/cuda/tile/thread_scope.cc
similarity index 99%
rename from src/backend/cuda/tile/exec_scope.cc
rename to src/backend/cuda/tile/thread_scope.cc
index 9ed4bf0baf..685efd12bd 100644
--- a/src/backend/cuda/tile/exec_scope.cc
+++ b/src/backend/cuda/tile/thread_scope.cc
@@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
-#include "exec_scope.h"
+#include "thread_scope.h"
#include <tvm/runtime/logging.h>
namespace tvm::backend::cuda {
diff --git a/src/backend/cuda/tile/exec_scope.h
b/src/backend/cuda/tile/thread_scope.h
similarity index 96%
rename from src/backend/cuda/tile/exec_scope.h
rename to src/backend/cuda/tile/thread_scope.h
index 8bc4b35142..654b287a69 100644
--- a/src/backend/cuda/tile/exec_scope.h
+++ b/src/backend/cuda/tile/thread_scope.h
@@ -16,9 +16,9 @@
* specific language governing permissions and limitations
* under the License.
*/
-#ifndef TVM_BACKEND_CUDA_TILE_EXEC_SCOPE_H_
-#define TVM_BACKEND_CUDA_TILE_EXEC_SCOPE_H_
-#include <tvm/tirx/tile/exec_scope.h>
+#ifndef TVM_BACKEND_CUDA_TILE_THREAD_SCOPE_H_
+#define TVM_BACKEND_CUDA_TILE_THREAD_SCOPE_H_
+#include <tvm/tirx/tile/thread_scope.h>
#include <string>
#include <utility>
diff --git a/src/backend/trn/codegen/target_kind.cc
b/src/backend/trn/codegen/target_kind.cc
index 573e71a2ce..47d3f6384c 100644
--- a/src/backend/trn/codegen/target_kind.cc
+++ b/src/backend/trn/codegen/target_kind.cc
@@ -28,6 +28,7 @@
#include <tvm/runtime/memory_scope.h>
#include <tvm/target/target.h>
#include <tvm/target/target_kind.h>
+#include <tvm/tirx/tile/thread_scope.h>
namespace tvm {
@@ -43,6 +44,12 @@ void RegisterTRNTransforms();
} // namespace tvm
TVM_FFI_STATIC_INIT_BLOCK() {
+ for (const auto& [name, rank] : {std::pair{"kernel", -1}, {"thread", 0}}) {
+ auto qualified = tvm::ffi::String("trn.") + name;
+ tvm::ffi::reflection::EnumDef<tvm::tirx::ThreadScopeObj>(qualified.c_str())
+ .set_attr(tvm::tirx::thread_scope_attr::kBackend,
tvm::ffi::String("trn"))
+ .set_attr(tvm::tirx::thread_scope_attr::kRank, rank);
+ }
for (const char* name : {"trn.sbuf", "trn.psum"}) {
tvm::ffi::reflection::EnumDef<tvm::runtime::MemoryScopeObj>(name).set_attr(
tvm::runtime::attr::kMemoryAllocProperty,
diff --git a/src/tirx/ir/layout/axis_registry.cc
b/src/tirx/ir/layout/axis_registry.cc
index ad9b43c639..df5bae9ce9 100644
--- a/src/tirx/ir/layout/axis_registry.cc
+++ b/src/tirx/ir/layout/axis_registry.cc
@@ -68,10 +68,12 @@ ffi::Optional<ffi::String> AxisNode::GetBackend() const {
return AxisAttr<ffi::String>(this, "backend");
}
-ffi::Optional<ExecScope> AxisNode::GetScope() const { return
AxisAttr<ExecScope>(this, "scope"); }
+ffi::Optional<ThreadScope> AxisNode::GetScope() const {
+ return AxisAttr<ThreadScope>(this, "scope");
+}
-ffi::Optional<ExecScope> AxisNode::GetSubscope() const {
- return AxisAttr<ExecScope>(this, "subscope");
+ffi::Optional<ThreadScope> AxisNode::GetSubscope() const {
+ return AxisAttr<ThreadScope>(this, "subscope");
}
ffi::Optional<FAxisFuser> AxisNode::GetFuser() const { return
AxisAttr<FAxisFuser>(this, "fuser"); }
diff --git a/src/tirx/ir/layout/tile_canonicalize.cc
b/src/tirx/ir/layout/tile_canonicalize.cc
index c22a118048..376875561f 100644
--- a/src/tirx/ir/layout/tile_canonicalize.cc
+++ b/src/tirx/ir/layout/tile_canonicalize.cc
@@ -86,8 +86,8 @@ TileLayout FuseAxesByScope(TileLayout layout) {
if (!scope_pair_opt.has_value() || !target.has_value()) {
return layout;
}
- auto subscope = scope_pair_opt.value().get<0>()->name();
- auto scope = scope_pair_opt.value().get<1>()->name();
+ auto subscope = scope_pair_opt.value().get<0>();
+ auto scope = scope_pair_opt.value().get<1>();
// Step 2: Create vectors for the new layout components
std::vector<Iter> shard;
diff --git a/src/tirx/ir/layout/tile_core.cc b/src/tirx/ir/layout/tile_core.cc
index 682fb51f1b..744c399718 100644
--- a/src/tirx/ir/layout/tile_core.cc
+++ b/src/tirx/ir/layout/tile_core.cc
@@ -327,57 +327,50 @@ bool TileLayoutNode::HasThreadAxis() const {
[](const Iter& iter) { return iter->axis->IsThreadAxis();
});
}
-ffi::Optional<ffi::Tuple<ExecScope, ExecScope>> TileLayoutNode::GetScope()
const {
+ffi::Optional<ffi::Tuple<ThreadScope, ThreadScope>> TileLayoutNode::GetScope()
const {
if (!HasThreadAxis()) return std::nullopt;
- std::unordered_map<ffi::String, ffi::String> scope_map;
- ffi::Optional<ffi::String> inner_most;
+ std::unordered_map<ThreadScope, ThreadScope, ffi::ObjectPtrHash,
ffi::ObjectPtrEqual> scope_map;
+ ffi::Optional<ThreadScope> inner_most;
ffi::Optional<ffi::String> backend;
auto check_axis = [&](const Axis& axis) {
if (!axis->IsThreadAxis()) return;
-
auto subscope_opt = axis->GetSubscope();
auto scope_opt = axis->GetScope();
TVM_FFI_ICHECK(subscope_opt.has_value() && scope_opt.has_value())
<< "Thread axis " << axis.name() << " has no subscope or scope";
-
+ ThreadScope subscope = subscope_opt.value();
+ ThreadScope scope = scope_opt.value();
+ TVM_FFI_CHECK(scope->backend() == subscope->backend(), ValueError)
+ << "Thread axis contains scopes from different backends";
if (backend.has_value()) {
- TVM_FFI_CHECK(backend.value() == subscope_opt.value()->backend,
ValueError)
+ TVM_FFI_CHECK(backend.value() == subscope->backend(), ValueError)
<< "Layout contains thread axes from different backends";
} else {
- backend = subscope_opt.value()->backend;
+ backend = subscope->backend();
}
- ffi::String subscope = subscope_opt.value()->name();
- ffi::String scope = scope_opt.value()->name();
-
- if (!inner_most.has_value() ||
- ScopeHigher(ExecScope(backend.value(), inner_most.value()),
subscope_opt.value()))
+ if (!inner_most.has_value() || ScopeHigher(inner_most.value(), subscope)) {
inner_most = subscope;
-
- auto it = scope_map.find(subscope);
- if (it == scope_map.end())
- scope_map[subscope] = scope;
- else
- TVM_FFI_ICHECK_EQ(it->second, scope)
- << "Ill-formed tile layout: conflicting scopes for " << subscope;
+ }
+ auto [it, inserted] = scope_map.emplace(subscope, scope);
+ TVM_FFI_ICHECK(inserted || it->second.same_as(scope))
+ << "Ill-formed tile layout: conflicting scopes for " << subscope;
};
-
for (const auto& iter : shard) check_axis(iter->axis);
for (const auto& iter : replica) check_axis(iter->axis);
for (const auto& [axis, off] : offset) check_axis(axis);
- ffi::String outer_most = inner_most.value();
+ ThreadScope outer_most = inner_most.value();
size_t count = 0;
for (auto it = scope_map.find(outer_most); it != scope_map.end();
it = scope_map.find(outer_most)) {
- count++;
+ TVM_FFI_ICHECK_LT(count, scope_map.size()) << "Ill-formed tile layout:
cyclic scope chain";
+ ++count;
outer_most = it->second;
}
-
TVM_FFI_ICHECK_EQ(count, scope_map.size()) << "Ill-formed tile layout:
disconnected scope chain";
- return Tuple<ExecScope, ExecScope>{ExecScope(backend.value(),
inner_most.value()),
- ExecScope(backend.value(), outer_most)};
+ return Tuple<ThreadScope, ThreadScope>{inner_most.value(), outer_most};
}
TileLayout TileLayoutNode::DefaultLayout(ffi::Array<PrimExpr> shape) {
@@ -393,7 +386,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef().def(
"tirx.TileLayoutGetScope",
- [](const TileLayout& layout) -> ffi::Optional<ffi::Tuple<ExecScope,
ExecScope>> {
+ [](const TileLayout& layout) -> ffi::Optional<ffi::Tuple<ThreadScope,
ThreadScope>> {
return layout->GetScope();
});
}
diff --git a/src/tirx/script/ir_builder/frame.cc
b/src/tirx/script/ir_builder/frame.cc
index 9e72399f23..d1559cc26b 100644
--- a/src/tirx/script/ir_builder/frame.cc
+++ b/src/tirx/script/ir_builder/frame.cc
@@ -24,7 +24,7 @@
#include <tvm/script/ir_builder/ir.h>
#include <tvm/tirx/function.h>
#include <tvm/tirx/script/ir_builder/frame.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <map>
diff --git a/src/tirx/script/ir_builder/ir.cc b/src/tirx/script/ir_builder/ir.cc
index e2dd1a34df..85d008ecce 100644
--- a/src/tirx/script/ir_builder/ir.cc
+++ b/src/tirx/script/ir_builder/ir.cc
@@ -29,7 +29,7 @@
#include <tvm/tirx/layout.h>
#include <tvm/tirx/script/ir_builder/ir.h>
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include "./utils.h"
diff --git a/src/tirx/script/printer/block.cc b/src/tirx/script/printer/block.cc
index c036f7b6b1..41ab52804a 100644
--- a/src/tirx/script/printer/block.cc
+++ b/src/tirx/script/printer/block.cc
@@ -20,7 +20,7 @@
#include <tvm/script/printer/doc_translator.h>
#include <tvm/tirx/function.h>
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <optional>
@@ -33,20 +33,19 @@ namespace details {
namespace {
-ffi::Optional<ExprDoc> ExecScopeDocTranslate(DocTranslatorObj*, ffi::AnyView
input,
- const ffi::Object*) {
+ffi::Optional<ExprDoc> ThreadScopeDocTranslate(DocTranslatorObj*, ffi::AnyView
input,
+ const ffi::Object*) {
const auto* scope =
- ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const
tirx::ExecScopeNode>(input);
+ ffi::details::AnyUnsafe::RawObjectPtrFromAnyViewAfterCheck<const
tirx::ThreadScopeObj>(input);
return NamespaceDoc("tirx")
- ->Attr(scope->backend)
- ->Attr("ExecScope")
+ ->Attr("ThreadScope")
->Call({LiteralDoc::Str(scope->name(), std::nullopt)});
}
TVM_FFI_STATIC_INIT_BLOCK() {
- ffi::reflection::TypeAttrDef<tirx::ExecScopeNode>().attr(
+ ffi::reflection::TypeAttrDef<tirx::ThreadScopeObj>().attr(
tvm::script::printer::type_attr::kDocTranslate,
- FDocTranslate::FromNative<&ExecScopeDocTranslate>());
+ FDocTranslate::FromNative<&ThreadScopeDocTranslate>());
}
} // namespace
diff --git a/src/tirx/script/printer/expr.cc b/src/tirx/script/printer/expr.cc
index 6db853e485..51cc83f69c 100644
--- a/src/tirx/script/printer/expr.cc
+++ b/src/tirx/script/printer/expr.cc
@@ -28,7 +28,7 @@
#include <tvm/tirx/op/abi.h>
#include <tvm/tirx/op_attr_types.h>
#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <algorithm>
#include <limits>
@@ -223,7 +223,7 @@ TVM_FFI_STATIC_INIT_BLOCK() {
using script::printer::RegisterScriptRepr;
RegisterScriptRepr<tirx::TensorTypeNode>();
RegisterScriptRepr<tirx::ComposeLayoutNode>();
- RegisterScriptRepr<tirx::ExecScopeNode>();
+ RegisterScriptRepr<tirx::ThreadScopeObj>();
RegisterScriptRepr<tirx::IndexMapNode>();
RegisterScriptRepr<tirx::IterNode>();
RegisterScriptRepr<tirx::FunctionNode>();
diff --git a/src/tirx/tile/context.cc b/src/tirx/tile/context.cc
index 57684739f7..c6531a4385 100644
--- a/src/tirx/tile/context.cc
+++ b/src/tirx/tile/context.cc
@@ -85,7 +85,7 @@ ffi::Optional<ffi::ObjectRef>
DispatchContextObj::SharedStateGet(ffi::String key
return ffi::Optional<ffi::ObjectRef>();
}
-DispatchContext::DispatchContext(Target target, ExecScope exec_scope,
+DispatchContext::DispatchContext(Target target, ThreadScope thread_scope,
ffi::Map<ffi::String, ffi::Tuple<PrimVar,
PrimExpr>> launch_params,
ffi::Map<Var, Range> var_range_map, bool
alloc_only,
ffi::Map<ffi::String, ffi::ObjectRef>
callbacks,
@@ -93,7 +93,13 @@ DispatchContext::DispatchContext(Target target, ExecScope
exec_scope,
ffi::Map<ffi::String, ffi::Array<PrimExpr>>
inter,
ffi::Map<ffi::String, ffi::Array<PrimExpr>>
intra,
ffi::String scope_kind) {
- auto n = ffi::make_object<DispatchContextObj>(std::move(target),
std::move(exec_scope));
+ auto n = ffi::make_object<DispatchContextObj>(std::move(target),
std::move(thread_scope));
+ TVM_FFI_CHECK(n->thread_scope->backend() == n->target->kind->name,
ValueError)
+ << "Thread scope " << n->thread_scope.name() << " does not match target "
+ << n->target->kind->name;
+ ffi::String local_name = n->thread_scope->local_name();
+ TVM_FFI_CHECK(scope_kind.empty() || scope_kind == local_name, ValueError)
+ << "scope_kind must match the thread scope's local name";
for (const auto& [tag, binding] : launch_params) {
PrimType var_ty = binding.get<0>().ty();
PrimType extent_ty = binding.get<1>().ty();
@@ -107,7 +113,7 @@ DispatchContext::DispatchContext(Target target, ExecScope
exec_scope,
n->shared_state = std::move(shared_state);
n->inter = std::move(inter);
n->intra = std::move(intra);
- n->scope_kind = std::move(scope_kind);
+ n->scope_kind = std::move(local_name);
data_ = std::move(n);
}
@@ -115,14 +121,14 @@ TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef()
.def("tirx.DispatchContext",
- [](Target target, ExecScope exec_scope,
+ [](Target target, ThreadScope thread_scope,
ffi::Map<ffi::String, ffi::Tuple<PrimVar, PrimExpr>>
launch_params,
ffi::Map<Var, Range> var_range_map, bool alloc_only,
ffi::Map<ffi::String, ffi::ObjectRef> callbacks,
ffi::Map<ffi::String, ffi::ObjectRef> shared_state,
ffi::Map<ffi::String, ffi::Array<PrimExpr>> inter,
ffi::Map<ffi::String, ffi::Array<PrimExpr>> intra, ffi::String
scope_kind) {
- return DispatchContext(target, exec_scope, launch_params,
var_range_map, alloc_only,
+ return DispatchContext(target, thread_scope, launch_params,
var_range_map, alloc_only,
callbacks, shared_state, inter, intra,
scope_kind);
})
.def_method("tirx.DispatchContextAddAllocTensor",
&DispatchContextObj::AddAllocTensor)
diff --git a/src/tirx/tile/exec_scope.cc b/src/tirx/tile/exec_scope.cc
deleted file mode 100644
index edc289cbc0..0000000000
--- a/src/tirx/tile/exec_scope.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/runtime/logging.h>
-#include <tvm/tirx/tile/exec_scope.h>
-
-#include <map>
-#include <string>
-#include <utility>
-
-namespace tvm::tirx {
-namespace {
-using ScopeKey = std::pair<std::string, std::string>;
-std::map<ScopeKey, int>& Scopes() {
- static std::map<ScopeKey, int> scopes;
- return scopes;
-}
-int Lookup(const ffi::String& backend, const ffi::String& name) {
- auto it = Scopes().find({std::string(backend), std::string(name)});
- TVM_FFI_CHECK(it != Scopes().end(), ValueError)
- << "Unknown execution scope: " << backend << "." << name;
- return it->second;
-}
-} // namespace
-
-void RegisterExecScope(const ffi::String& backend, const ffi::String& name,
int rank) {
- Scopes().insert_or_assign({std::string(backend), std::string(name)}, rank);
-}
-int ExecScopeNode::rank() const { return Lookup(backend, scope_name); }
-ExecScope::ExecScope(ffi::String backend, ffi::String name) {
- Lookup(backend, name);
- auto n = ffi::make_object<ExecScopeNode>();
- n->backend = std::move(backend);
- n->scope_name = std::move(name);
- data_ = std::move(n);
-}
-bool ScopeHigher(const ExecScope& a, const ExecScope& b) {
- TVM_FFI_CHECK(a->backend == b->backend, ValueError)
- << "Cannot compare execution scopes from different backends";
- return a->rank() < b->rank();
-}
-TVM_FFI_STATIC_INIT_BLOCK() {
- ExecScopeNode::RegisterReflection();
- namespace refl = tvm::ffi::reflection;
- refl::GlobalDef()
- .def("tirx.ExecScope",
- [](ffi::String backend, ffi::String name) {
- return ExecScope(std::move(backend), std::move(name));
- })
- .def("tirx.RegisterExecScope", RegisterExecScope);
-}
-} // namespace tvm::tirx
diff --git a/src/tirx/tile/lower.cc b/src/tirx/tile/lower.cc
index cd0f2271e9..6f8cfabc49 100644
--- a/src/tirx/tile/lower.cc
+++ b/src/tirx/tile/lower.cc
@@ -39,7 +39,7 @@
#include <tvm/tirx/stmt.h>
#include <tvm/tirx/stmt_functor.h>
#include <tvm/tirx/tile/dispatch.h>
-#include <tvm/tirx/tile/exec_scope.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <tvm/tirx/transform.h>
#include <limits>
@@ -487,12 +487,12 @@ class TileDispatcher : public StmtExprMutator {
op->op.as_or_throw<Op>().Validate(op);
TVM_FFI_CHECK(op->attrs.has_value(), ValueError)
<< "A tile instruction must have scope attributes";
- auto scope_name =
- ffi::reflection::FieldGetter(op->attrs->GetTypeKey(),
"scope")(op->attrs.value())
- .cast<ffi::String>();
- ExecScope scope(target_->kind->name, scope_name);
+ auto scope = ffi::reflection::FieldGetter(op->attrs->GetTypeKey(),
"scope")(op->attrs.value())
+ .cast<ThreadScope>();
+ TVM_FFI_CHECK(scope->backend() == target_->kind->name, ValueError)
+ << "Thread scope " << scope.name() << " does not match target " <<
target_->kind->name;
ffi::Map<ffi::String, ffi::Array<PrimExpr>> inter_map, intra_map;
- ffi::String scope_kind = scope->name();
+ ffi::String scope_kind = scope->local_name();
if (cuda_analysis_) {
auto split = cuda_analysis_->Split(scope);
inter_map = std::move(split.first);
diff --git a/src/tirx/tile/op.cc b/src/tirx/tile/op.cc
index 6f1f4f268f..634f2d1afd 100644
--- a/src/tirx/tile/op.cc
+++ b/src/tirx/tile/op.cc
@@ -24,23 +24,25 @@
#include <tvm/ir/prim/op.h>
#include <tvm/tirx/tile/op.h>
+#include <tvm/tirx/tile/thread_scope.h>
#include <utility>
namespace tvm::tirx::tile {
struct ScopeAttrs : public AttrsNode {
- ffi::String scope;
+ ScopeAttrs() : scope(ffi::UnsafeInit{}) {}
+ ThreadScope scope;
static void RegisterReflection() {
namespace refl = ffi::reflection;
- refl::ObjectDef<ScopeAttrs>().def_ro("scope", &ScopeAttrs::scope,
-
refl::DefaultValue(ffi::String("thread")));
+ refl::ObjectDef<ScopeAttrs>().def_ro("scope", &ScopeAttrs::scope);
}
TVM_FFI_DECLARE_OBJECT_INFO_FINAL("tirx.tensor.ScopeAttrs", ScopeAttrs,
AttrsNode);
};
struct TrnAttrs : public AttrsNode {
- ffi::String scope;
+ TrnAttrs() : scope(ffi::UnsafeInit{}) {}
+ ThreadScope scope;
ffi::Optional<int64_t> max_inst_size;
ffi::String opcode;
ffi::String reduce_op;
@@ -52,7 +54,7 @@ struct TrnAttrs : public AttrsNode {
static void RegisterReflection() {
namespace refl = ffi::reflection;
refl::ObjectDef<TrnAttrs>()
- .def_ro("scope", &TrnAttrs::scope,
refl::DefaultValue(ffi::String("thread")))
+ .def_ro("scope", &TrnAttrs::scope)
.def_ro("max_inst_size", &TrnAttrs::max_inst_size,
refl::DefaultValue(ffi::Optional<int64_t>(std::nullopt)))
.def_ro("opcode", &TrnAttrs::opcode,
refl::DefaultValue(ffi::String("")))
diff --git a/src/tirx/tile/thread_scope.cc b/src/tirx/tile/thread_scope.cc
new file mode 100644
index 0000000000..930c219d0c
--- /dev/null
+++ b/src/tirx/tile/thread_scope.cc
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <tvm/ffi/cast.h>
+#include <tvm/ffi/extra/serialization.h>
+#include <tvm/ffi/reflection/registry.h>
+#include <tvm/tirx/tile/thread_scope.h>
+
+#include <string>
+
+namespace tvm::tirx {
+namespace refl = ffi::reflection;
+namespace {
+template <typename T>
+T ScopeAttr(const ThreadScopeObj* scope, refl::AttrKey<T> key) {
+ auto state = refl::TypeAttrColumn(
+
ffi::type_attr::kEnumState)[ThreadScopeObj::_GetOrAllocRuntimeTypeIndex()]
+ .cast<ffi::EnumState>();
+ auto column = state->attrs.Get(key.name);
+ auto value = key.ValueFrom(
+ column ?
column->Get(ffi::GetRef<ThreadScope>(scope)).value_or(ffi::Any()) : ffi::Any());
+ TVM_FFI_CHECK(value.has_value(), ValueError)
+ << "Thread scope " << scope->name() << " has no " << key.name << "
attribute";
+ return value.value();
+}
+} // namespace
+
+ffi::String ThreadScopeObj::backend() const { return ScopeAttr(this,
thread_scope_attr::kBackend); }
+ffi::String ThreadScopeObj::local_name() const {
+ const std::string prefix = std::string(backend()) + ".";
+ const std::string name(_str_index);
+ TVM_FFI_CHECK(name.size() > prefix.size() && name.compare(0, prefix.size(),
prefix) == 0,
+ ValueError)
+ << "Thread scope " << name << " must be named as backend.scope";
+ return name.substr(prefix.size());
+}
+int64_t ThreadScopeObj::rank() const { return ScopeAttr(this,
thread_scope_attr::kRank); }
+
+ThreadScope ThreadScope::Get(const ffi::String& name) {
+ auto scope =
ffi::EnumObj::_GetByStrIndex<ThreadScopeObj>(name).as_or_throw<ThreadScope>();
+ scope->local_name();
+ scope->rank();
+ return scope;
+}
+
+bool ScopeHigher(const ThreadScope& a, const ThreadScope& b) {
+ TVM_FFI_CHECK(a->backend() == b->backend(), ValueError)
+ << "Cannot compare thread scopes from different backends";
+ return a->rank() < b->rank();
+}
+
+TVM_FFI_STATIC_INIT_BLOCK() {
+ refl::ObjectDef<ThreadScopeObj>(refl::init(false));
+ refl::TypeAttrDef<ThreadScopeObj>()
+ .def(ffi::type_attr::kDataToJson.name,
+ [](const ThreadScopeObj* scope) -> ffi::String { return
scope->name(); })
+ .def(ffi::type_attr::kDataFromJson.name, ThreadScope::Get);
+ refl::GlobalDef()
+ .def("tirx.ThreadScopeGet", ThreadScope::Get)
+ .def("tirx.ThreadScopeGetBackend", [](ThreadScope scope) { return
scope->backend(); })
+ .def("tirx.ThreadScopeGetLocalName", [](ThreadScope scope) { return
scope->local_name(); })
+ .def("tirx.ThreadScopeGetRank", [](ThreadScope scope) { return
scope->rank(); })
+ .def("tirx.ScopeHigher", ScopeHigher);
+}
+} // namespace tvm::tirx
diff --git a/tests/cpp/tirx/thread_scope_test.cc
b/tests/cpp/tirx/thread_scope_test.cc
new file mode 100644
index 0000000000..c6530ec9e2
--- /dev/null
+++ b/tests/cpp/tirx/thread_scope_test.cc
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+#include <gtest/gtest.h>
+#include <tvm/ffi/extra/serialization.h>
+#include <tvm/ffi/reflection/creator.h>
+#include <tvm/ir/op.h>
+#include <tvm/target/target_kind.h>
+#include <tvm/tirx/tile/dispatch.h>
+#include <tvm/tirx/transform.h>
+
+#include <memory>
+
+namespace tvm::tirx {
+namespace {
+namespace refl = ffi::reflection;
+
+void RegisterTestScopes() {
+ static const bool registered = [] {
+ // Register the inner scope first; enum indices must not define ordering.
+ refl::EnumDef<ThreadScopeObj>("thread_scope_test.inner")
+ .set_attr(thread_scope_attr::kBackend,
ffi::String("thread_scope_test"))
+ .set_attr(thread_scope_attr::kRank, 10);
+ refl::EnumDef<ThreadScopeObj>("thread_scope_test.outer")
+ .set_attr(thread_scope_attr::kBackend,
ffi::String("thread_scope_test"))
+ .set_attr(thread_scope_attr::kRank, -10);
+ return true;
+ }();
+ (void)registered;
+}
+
+TEST(ThreadScope, NativeRegistrationIdentityAndRank) {
+ RegisterTestScopes();
+ ThreadScope cuda("cuda.thread");
+ ThreadScope trn("trn.thread");
+ EXPECT_EQ(cuda->backend(), "cuda");
+ EXPECT_EQ(cuda->rank(), 4);
+ EXPECT_EQ(trn->backend(), "trn");
+ EXPECT_EQ(trn->rank(), 0);
+ EXPECT_FALSE(cuda.same_as(trn));
+ EXPECT_THROW(ScopeHigher(cuda, trn), ffi::Error);
+ ThreadScope inner("thread_scope_test.inner");
+ ThreadScope outer("thread_scope_test.outer");
+ EXPECT_TRUE(inner.same_as(ThreadScope::Get(inner.name())));
+ EXPECT_EQ(inner->backend(), "thread_scope_test");
+ EXPECT_EQ(inner->local_name(), "inner");
+ EXPECT_EQ(inner->rank(), 10);
+ EXPECT_TRUE(ScopeHigher(outer, inner));
+ EXPECT_FALSE(ScopeHigher(inner, outer));
+ EXPECT_FALSE(ScopeHigher(inner, inner));
+ EXPECT_GT(outer->_int_index, inner->_int_index);
+ EXPECT_TRUE(ffi::Any(inner.name()).cast<ThreadScope>().same_as(inner));
+ EXPECT_THROW(ThreadScope("thread_scope_test.missing"), ffi::Error);
+ EXPECT_THROW(refl::EnumDef<ThreadScopeObj>(inner.name().c_str()),
ffi::Error);
+
EXPECT_TRUE(ffi::FromJSONGraph(ffi::ToJSONGraph(inner)).cast<ThreadScope>().same_as(inner));
+}
+
+TEST(ThreadScope, TypedCallAndDispatchWithoutPython) {
+ RegisterTestScopes();
+ TargetKindDef("thread_scope_test").set_default_device_type(kDLCPU);
+ Target target("thread_scope_test");
+ ThreadScope scope("thread_scope_test.inner");
+ auto attrs = refl::ObjectCreator("tirx.tensor.ScopeAttrs")({{"scope",
scope}}).cast<Attrs>();
+ auto invoked = std::make_shared<int>(0);
+ OpDef("thread_scope_test.instruction")
+ .signature()
+ .set_attr<TFixedReturnType>(op_attr::kFixedReturnType, PrimType::Void())
+ .set_attr<tile::FTileOpDispatch>(
+ tile::op_attr::kTileOpDispatch,
+ [scope, invoked](const Call& call, const tile::DispatchContext&
context) {
+ EXPECT_TRUE(context->thread_scope.same_as(scope));
+ EXPECT_EQ(context->scope_kind, "inner");
+ EXPECT_TRUE(refl::FieldGetter(call->attrs->GetTypeKey(),
"scope")(call->attrs.value())
+ .cast<ThreadScope>()
+ .same_as(scope));
+ ++*invoked;
+ return Function({}, SeqStmt(Evaluate(37)));
+ });
+ Call call(PrimType::Void(), Op::Get("thread_scope_test.instruction"), {},
attrs);
+ Function func({}, SeqStmt(Evaluate(call)), VoidType(), DictAttrs({{"target",
target}}));
+ GlobalVar main("main");
+ auto result = transform::TileDispatch()(IRModule({{main, func}}));
+ auto lowered = result->Lookup(main).as_or_throw<Function>();
+ EXPECT_EQ(*invoked, 1);
+ EXPECT_EQ(
+
lowered->body.value()->seq[0].as_or_throw<Evaluate>()->value.as_or_throw<IntImm>()->value,
+ 37);
+ EXPECT_THROW(refl::ObjectCreator("tirx.tensor.ScopeAttrs")({}), ffi::Error);
+ EXPECT_THROW(refl::ObjectCreator("tirx.tensor.ScopeAttrs")({{"scope",
ffi::String("inner")}}),
+ ffi::Error);
+}
+} // namespace
+} // namespace tvm::tirx
diff --git a/tests/python/tirx/script/test_tirx_script_basic_usage.py
b/tests/python/tirx/script/test_tirx_script_basic_usage.py
index 822c77d1a6..52207cd308 100644
--- a/tests/python/tirx/script/test_tirx_script_basic_usage.py
+++ b/tests/python/tirx/script/test_tirx_script_basic_usage.py
@@ -359,16 +359,23 @@ def test_roundtrip_unary_inplace():
code = test.script()
# Opcode and explicit operand aliases survive printing.
- expected = 'T.cuda.tile.apply("ex2.approx.ftz.f32", (A[0:32], A[0:32]),
(), scope="warp")'
+ expected = (
+ 'T.cuda.tile.apply("ex2.approx.ftz.f32", (A[0:32], A[0:32]), '
+ '(), scope=T.ThreadScope("cuda.warp"))'
+ )
assert expected in code, (
f"expected explicit exp2, got:\n{code}"
)
- assert 'T.cuda.tile.apply("sqrt.rn.f32", (A[32:64], A[32:64]), (),
scope="warp")' in code, (
+ expected = (
+ 'T.cuda.tile.apply("sqrt.rn.f32", (A[32:64], A[32:64]), '
+ '(), scope=T.ThreadScope("cuda.warp"))'
+ )
+ assert expected in code, (
f"expected explicit sqrt, got:\n{code}"
)
expected = (
'T.cuda.tile.apply("div.rn.f32", (A[64:96], T.float32(1.0), A[64:96]),
'
- '(), scope="warp")'
+ '(), scope=T.ThreadScope("cuda.warp"))'
)
assert expected in code, (
f"expected explicit reciprocal, got:\n{code}"
@@ -395,7 +402,10 @@ def test_roundtrip_unary_different_dst_src():
# fmt: on
code = test.script()
- expected = 'T.cuda.tile.apply("ex2.approx.ftz.f32", (A[0:32], B[0:32]),
(), scope="warp")'
+ expected = (
+ 'T.cuda.tile.apply("ex2.approx.ftz.f32", (A[0:32], B[0:32]), '
+ '(), scope=T.ThreadScope("cuda.warp"))'
+ )
assert expected in code, (
f"different dst/src should keep both:\n{code}"
)
diff --git a/tests/python/tirx/script/test_tirx_script_gpu.py
b/tests/python/tirx/script/test_tirx_script_gpu.py
index cd56a746f4..514959c0c0 100644
--- a/tests/python/tirx/script/test_tirx_script_gpu.py
+++ b/tests/python/tirx/script/test_tirx_script_gpu.py
@@ -99,7 +99,7 @@ def test_roundtrip_scopeid_deferred():
assert_structural_equal(test, from_source(code))
-def test_exec_scope_filter_guard_roundtrip():
+def test_thread_scope_filter_guard_roundtrip():
@T.function(private=True)
def test(A: T.Tensor((1,), "float32", scope="global")) -> None:
T.device_entry(launch=T.cuda.LaunchConfig(grid=(1,), block=(128,)))
diff --git a/tests/python/tirx/script/test_tirx_script_printer.py
b/tests/python/tirx/script/test_tirx_script_printer.py
index 5bd62ce716..7c91c15018 100644
--- a/tests/python/tirx/script/test_tirx_script_printer.py
+++ b/tests/python/tirx/script/test_tirx_script_printer.py
@@ -726,7 +726,8 @@ def test_print_kwargs_schedule_op_full_code():
@T.function
def test():
A = T.alloc_tensor(ty_args=[T.Tensor((16,), T.float32)])
- T.cuda.tile.apply("mov.b32", (A[0:16], T.float32(1.25)), (),
scope="warp")"""
+ T.cuda.tile.apply("mov.b32", (A[0:16], T.float32(1.25)), (),
scope=T.ThreadScope("cuda.warp"))
+""".rstrip()
code = test.script()
assert code == expected
assert from_source(code).script() == code
diff --git a/tests/python/tirx/test_exec_scope.py
b/tests/python/tirx/test_exec_scope.py
deleted file mode 100644
index 27eabab8f9..0000000000
--- a/tests/python/tirx/test_exec_scope.py
+++ /dev/null
@@ -1,43 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance
-# with the License. You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing,
-# software distributed under the License is distributed on an
-# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-# KIND, either express or implied. See the License for the
-# specific language governing permissions and limitations
-# under the License.
-import pytest
-
-from tvm.tirx.exec_scope import ExecScope
-
-
-def test_exec_scope_create():
- def is_trivial_scope(scope, name):
- return isinstance(scope, ExecScope) and scope.name == name
-
- thread = ExecScope("cuda", "thread")
- warp = ExecScope("cuda", "warp")
- wg = ExecScope("cuda", "warpgroup")
- cta = ExecScope("cuda", "cta")
- cluster = ExecScope("cuda", "cluster")
-
- assert is_trivial_scope(thread, "thread")
- assert is_trivial_scope(warp, "warp")
- assert is_trivial_scope(wg, "warpgroup")
- assert is_trivial_scope(cta, "cta")
- assert is_trivial_scope(cluster, "cluster")
-
- with pytest.raises(Exception, match="Unknown execution scope"):
- ExecScope("cuda", "aaa")
-
-
-if __name__ == "__main__":
- test_exec_scope_create()
diff --git a/tests/python/tirx/test_layout.py b/tests/python/tirx/test_layout.py
index f8e33cb0a9..3f646d4894 100644
--- a/tests/python/tirx/test_layout.py
+++ b/tests/python/tirx/test_layout.py
@@ -76,8 +76,8 @@ def test_axis():
assert CUDAAxis.TCol.is_memory()
assert CUDAAxis.TLane.is_memory()
- assert CUDAAxis.bx.get_scope().name == "kernel"
- assert CUDAAxis.bx.get_subscope().name == "cta"
+ assert CUDAAxis.bx.get_scope().name == "cuda.kernel"
+ assert CUDAAxis.bx.get_subscope().name == "cuda.cta"
def test_constructor():
@@ -201,22 +201,22 @@ def test_verify_well_formed():
layout = TileLayout(S[(8, 4, 2) : (4 @ laneid, 1 @ laneid, 1)])
res = layout.get_scope()
assert res is not None
- assert res[0].name == "thread"
- assert res[1].name == "warp"
+ assert res[0].name == "cuda.thread"
+ assert res[1].name == "cuda.warp"
assert layout.verify_well_formed()
layout = TileLayout(S[8 : 4 @ laneid] + R[4 : 1 @ laneid])
res = layout.get_scope()
assert res is not None
- assert res[0].name == "thread"
- assert res[1].name == "warp"
+ assert res[0].name == "cuda.thread"
+ assert res[1].name == "cuda.warp"
assert layout.verify_well_formed()
layout = TileLayout(S[(8, 4, 2) : (4 @ laneid, 1 @ laneid, 1)])
res = layout.get_scope()
assert res is not None
- assert res[0].name == "thread"
- assert res[1].name == "warp"
+ assert res[0].name == "cuda.thread"
+ assert res[1].name == "cuda.warp"
assert layout.verify_well_formed()
layout = TileLayout(
@@ -224,8 +224,8 @@ def test_verify_well_formed():
)
res = layout.get_scope()
assert res is not None
- assert res[0].name == "thread"
- assert res[1].name == "cta"
+ assert res[0].name == "cuda.thread"
+ assert res[1].name == "cuda.cta"
assert layout.verify_well_formed()
layout = TileLayout(
@@ -233,8 +233,8 @@ def test_verify_well_formed():
)
res = layout.get_scope()
assert res is not None
- assert res[0].name == "thread"
- assert res[1].name == "warpgroup"
+ assert res[0].name == "cuda.thread"
+ assert res[1].name == "cuda.warpgroup"
assert layout.verify_well_formed()
layout = TileLayout(S[(2, 8, 2, 4, 2) : (2 @ wgid, 4 @ laneid, 1 @
wgid, 1 @ laneid, 1)])
diff --git a/tests/python/tirx/test_op.py b/tests/python/tirx/test_op.py
index 42b1e41065..87e5fff2d1 100644
--- a/tests/python/tirx/test_op.py
+++ b/tests/python/tirx/test_op.py
@@ -36,7 +36,7 @@ def test_instruction_is_a_void_call():
assert call.ty == tvm.ir.PrimType("void")
assert call.op.get_attr("TCallEffectKind") == 3
assert callable(call.op.get_attr("FTileOpDispatch"))
- assert call.attrs.scope == "warp"
+ assert call.attrs.scope.same_as(T.cuda.ThreadScope("warp"))
assert call.args[0].value == "ld.global.v4.b32"
assert not hasattr(call, "config")
assert not hasattr(call, "workspace")
diff --git a/tests/python/tirx/test_thread_scope.py
b/tests/python/tirx/test_thread_scope.py
new file mode 100644
index 0000000000..37e8572dba
--- /dev/null
+++ b/tests/python/tirx/test_thread_scope.py
@@ -0,0 +1,143 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import os
+import pickle
+import subprocess
+import sys
+
+import pytest
+import tvm_ffi
+from tvm_ffi.dataclasses import Enum
+
+import tvm
+from tvm.ir import Call, Evaluate, make_node
+from tvm.script import tirx as T
+from tvm.tirx import Function, decl_tensor
+from tvm.tirx.thread_scope import ThreadScope
+from tvm.tirx.tile import DispatchContext
+from tvm.tirx.tile.instruction import TensorCall
+
+
[email protected](
+ "backend,names",
+ [
+ ("cuda", ("kernel", "cluster", "cta", "warpgroup", "warp", "thread")),
+ ("trn", ("kernel", "thread")),
+ ],
+)
+def test_enum_identity_and_explicit_ranks(backend, names):
+ for rank, name in enumerate(names, start=-1):
+ scope = ThreadScope(f"{backend}.{name}")
+ assert isinstance(scope, Enum)
+ assert scope.same_as(ThreadScope.get(scope.name))
+ assert scope.same_as(getattr(T, backend).ThreadScope(name))
+ assert scope.name == f"{backend}.{name}"
+ assert scope.local_name == name
+ assert scope.backend == backend
+ assert scope.rank == rank
+ assert ThreadScope.def_attr("backend")[scope] == backend
+ assert ThreadScope.def_attr("rank")[scope] == rank
+ assert hash(scope) == hash(ThreadScope(scope.name))
+ assert not ThreadScope("cuda.thread").same_as(ThreadScope("trn.thread"))
+
+
+def test_hierarchy_comparison_and_lookup_errors():
+ higher = tvm_ffi.get_global_func("tirx.ScopeHigher")
+ assert higher(ThreadScope("cuda.kernel"), ThreadScope("cuda.thread"))
+ assert not higher(ThreadScope("cuda.thread"), ThreadScope("cuda.kernel"))
+ assert not higher(ThreadScope("cuda.warp"), ThreadScope("cuda.warp"))
+ with pytest.raises(ValueError, match="different backends"):
+ higher(ThreadScope("cuda.thread"), ThreadScope("trn.thread"))
+ for name in ("warp", "cuda.unknown", "trn.warp", ""):
+ with pytest.raises(ValueError):
+ ThreadScope(name)
+
+
[email protected]("name", ["cuda.kernel", "cuda.warp", "trn.thread"])
+def test_scope_roundtrips_keep_singleton(name):
+ scope = ThreadScope(name)
+ assert tvm.ir.load_json(tvm.ir.save_json(scope)).same_as(scope)
+ assert pickle.loads(pickle.dumps(scope)).same_as(scope)
+ text = scope.script()
+ assert f'T.ThreadScope("{name}")' in text
+ assert eval(text, {"T": T}).same_as(scope)
+
+
[email protected]("scope", ["warp", "cuda.warp",
T.cuda.ThreadScope("warp")])
+def test_instruction_normalizes_scope_before_constructing_call(scope):
+ tensor = decl_tensor((16,), "float32", scope="local", layout="default")
+ call = T.cuda.tile.apply(T.ptx["sqrt.rn.f32"], tensor, tensor, scope=scope)
+ tag = ThreadScope("cuda.warp")
+ assert call.attrs.scope.same_as(tag)
+ assert TensorCall.decode(call).scope.same_as(tag)
+ default = T.cuda.tile.apply(T.ptx["sqrt.rn.f32"], tensor, tensor)
+ assert default.attrs.scope.same_as(ThreadScope("cuda.thread"))
+ context = DispatchContext(tvm.target.Target("cuda"), tag, {}, {})
+ assert context.thread_scope.same_as(tag)
+ assert context.scope_kind == "warp"
+ with pytest.raises(ValueError, match="scope_kind"):
+ DispatchContext(tvm.target.Target("cuda"), tag, {}, {},
scope_kind="thread")
+
+
+def test_raw_scope_attrs_are_required_and_typed():
+ for schema in ("ScopeAttrs", "TrnAttrs"):
+ with pytest.raises(TypeError, match="scope"):
+ make_node(f"tirx.tensor.{schema}")
+ for invalid in (None, "thread", 1):
+ with pytest.raises((TypeError, ValueError)):
+ make_node(f"tirx.tensor.{schema}", scope=invalid)
+ attrs = make_node(f"tirx.tensor.{schema}", scope="cuda.thread")
+ assert attrs.scope.same_as(ThreadScope("cuda.thread"))
+ assert
tvm.ir.load_json(tvm.ir.save_json(attrs)).scope.same_as(attrs.scope)
+
+
+def test_cross_backend_scopes_are_rejected_at_each_boundary():
+ tensor = decl_tensor((16,), "float32", scope="local", layout="default")
+ trn_scope = ThreadScope("trn.thread")
+ for scope in (trn_scope, "trn.thread"):
+ with pytest.raises(ValueError, match="belongs to trn"):
+ T.cuda.tile.apply(T.ptx["sqrt.rn.f32"], tensor, tensor,
scope=scope)
+ call = T.cuda.tile.apply(T.ptx["sqrt.rn.f32"], tensor, tensor)
+ attrs = make_node("tirx.tensor.ScopeAttrs", scope=trn_scope)
+ raw = Call(call.op, call.args, attrs=attrs, ty=call.ty)
+ with pytest.raises(ValueError, match="belongs to trn"):
+ raw.validate()
+ with pytest.raises(ValueError, match="does not match target"):
+ DispatchContext(tvm.target.Target("cuda"), trn_scope, {}, {})
+ trn_tensor = decl_tensor((16,), "float32", scope="trn.sbuf",
layout="default")
+ trn_call = T.trn.tile.tensortensor(trn_tensor, trn_tensor, trn_tensor)
+ func = Function([], Evaluate(trn_call)).with_attr("target",
tvm.target.Target("cuda"))
+ with pytest.raises(ValueError, match="does not match target"):
+ tvm.tirx.transform.TileDispatch()(tvm.IRModule({"main": func}))
+
+
+def test_runtime_only_import_and_removed_interfaces():
+ env = os.environ.copy()
+ env["TVM_USE_RUNTIME_LIB"] = "1"
+ subprocess.run(
+ [
+ sys.executable,
+ "-c",
+ "import tvm; from tvm.tirx.thread_scope import ThreadScope; "
+ "assert tvm.base._RUNTIME_ONLY",
+ ],
+ env=env,
+ check=True,
+ )
+ assert not hasattr(tvm.tirx, "ExecScope")
+ assert not hasattr(T.cuda, "ExecScope")
+ assert tvm_ffi.get_global_func("tirx.RegisterExecScope",
allow_missing=True) is None
diff --git a/tests/python/tirx/test_tile_namespace.py
b/tests/python/tirx/test_tile_namespace.py
index 672340e8d7..543175e79b 100644
--- a/tests/python/tirx/test_tile_namespace.py
+++ b/tests/python/tirx/test_tile_namespace.py
@@ -28,9 +28,9 @@ from tvm.script import tirx as T
from tvm.script.ir_builder import IRBuilder
from tvm.testing import env
from tvm.tirx import decl_tensor
-from tvm.tirx.exec_scope import ExecScope
from tvm.tirx.layout import Axis, S, TileLayout
from tvm.tirx.script import ir_builder as I
+from tvm.tirx.thread_scope import ThreadScope
def test_generic_layout_without_backend_autoload():
@@ -67,8 +67,8 @@ def test_backend_axis_and_scope_identity():
assert Axis.m.backend is None
assert T.cuda.Axis.laneid.backend == "cuda"
assert T.trn.Axis.P.backend == "trn"
- assert T.cuda.Axis.laneid.get_scope().name == "warp"
- assert T.cuda.Axis.laneid.get_subscope().name == "thread"
+ assert T.cuda.Axis.laneid.get_scope().name == "cuda.warp"
+ assert T.cuda.Axis.laneid.get_subscope().name == "cuda.thread"
assert not T.trn.Axis.P.is_thread()
with pytest.raises(AttributeError):
_ = Axis.laneid
@@ -76,20 +76,20 @@ def test_backend_axis_and_scope_identity():
_ = T.cuda.Axis.P
with pytest.raises(AttributeError):
_ = T.trn.Axis.laneid
- cuda = T.cuda.ExecScope("thread")
- trn = T.trn.ExecScope("thread")
+ cuda = T.cuda.ThreadScope("thread")
+ trn = T.trn.ThreadScope("thread")
assert cuda.backend == "cuda" and trn.backend == "trn"
assert not tvm_ffi.structural_equal(cuda, trn)
assert_structural_equal(cuda, load_json(save_json(cuda)))
assert_structural_equal(trn, load_json(save_json(trn)))
- for backend, factory in (("cuda", T.cuda.ExecScope), ("trn",
T.trn.ExecScope)):
+ for backend, factory in (("cuda", T.cuda.ThreadScope), ("trn",
T.trn.ThreadScope)):
root = factory("kernel")
- assert_structural_equal(root, ExecScope(backend, "kernel"))
+ assert_structural_equal(root, ThreadScope(f"{backend}.kernel"))
assert_structural_equal(root, load_json(save_json(root)))
- assert f'T.{backend}.ExecScope("kernel")' in root.script()
- assert_structural_equal(T.cuda.Axis.bx.get_scope(),
T.cuda.ExecScope("kernel"))
- with pytest.raises(ValueError, match="Unknown execution scope"):
- T.trn.ExecScope("warp")
+ assert f'T.ThreadScope("{backend}.kernel")' in root.script()
+ assert_structural_equal(T.cuda.Axis.bx.get_scope(),
T.cuda.ThreadScope("kernel"))
+ with pytest.raises(ValueError):
+ T.trn.ThreadScope("warp")
@pytest.mark.parametrize(
@@ -212,7 +212,7 @@ def test_tile_call_rejects_cross_backend_scope():
tensor = decl_tensor((8,), "float32", scope="local", layout="default")
with pytest.raises(ValueError, match="scope belongs to trn"):
T.cuda.tile.apply(
- T.ptx["add.f32"], tensor[:], tensor[:], tensor[:],
scope=T.trn.ExecScope("thread")
+ T.ptx["add.f32"], tensor[:], tensor[:], tensor[:],
scope=T.trn.ThreadScope("thread")
)
diff --git a/tests/python/tirx/test_tirx_axis_enum.py
b/tests/python/tirx/test_tirx_axis_enum.py
index e6e22d433c..7ba649119c 100644
--- a/tests/python/tirx/test_tirx_axis_enum.py
+++ b/tests/python/tirx/test_tirx_axis_enum.py
@@ -39,8 +39,8 @@ def test_axis_enum_identity_and_attributes():
assert hash(axis) == hash(Axis.get("laneid"))
assert Axis.def_attr("thread")[axis] is True
assert Axis.m.is_memory()
- assert CUDAAxis.bx.get_scope().name == "kernel"
- assert CUDAAxis.bx.get_subscope().name == "cta"
+ assert CUDAAxis.bx.get_scope().name == "cuda.kernel"
+ assert CUDAAxis.bx.get_subscope().name == "cuda.cta"
assert TileLayout(S[4 : 1 @ axis]).shard[0].axis.same_as(axis)
diff --git a/tests/python/tirx/tile/cuda/test_add_sub_mul_div.py
b/tests/python/tirx/tile/cuda/test_add_sub_mul_div.py
index 3533258446..6e557ef241 100644
--- a/tests/python/tirx/tile/cuda/test_add_sub_mul_div.py
+++ b/tests/python/tirx/tile/cuda/test_add_sub_mul_div.py
@@ -192,17 +192,17 @@ def test_binary_non_commutative_const_lhs(op_type):
@pytest.mark.gpu
@pytest.mark.skipif(not env.has_cuda(), reason="need cuda")
[email protected]("exec_scope", ["warp", "warpgroup"])
[email protected]("thread_scope", ["warp", "warpgroup"])
@pytest.mark.parametrize("op_type", ["add", "mul"])
-def test_binary_op_shared_subcta_scope(exec_scope, op_type):
- n_warps = 4 if exec_scope == "warpgroup" else 1
+def test_binary_op_shared_subcta_scope(thread_scope, op_type):
+ n_warps = 4 if thread_scope == "warpgroup" else 1
shape = (n_warps * 32, 8)
size = math.prod(shape)
# The old test executes warp 5 / warpgroup 1 inside a 256-thread block.
- group_shape = (n_warps, 32, 8, 1) if exec_scope == "warpgroup" else (32,
8, 1)
+ group_shape = (n_warps, 32, 8, 1) if thread_scope == "warpgroup" else (32,
8, 1)
reg_layout = (
TileLayout(S[(n_warps, 32, 8, 1) : (1 @ wid_in_wg, 1 @ laneid, 1, 1)])
- if exec_scope == "warpgroup"
+ if thread_scope == "warpgroup"
else TileLayout(S[(32, 8, 1) : (1 @ laneid, 1, 1)])
)
@@ -222,32 +222,32 @@ def test_binary_op_shared_subcta_scope(exec_scope,
op_type):
T.cuda.cta_sync()
a = T.alloc_local(group_shape, "float16", layout=reg_layout)
b = T.alloc_local(group_shape, "float16", layout=reg_layout)
- if T.constexpr(exec_scope == "warpgroup"):
+ if T.constexpr(thread_scope == "warpgroup"):
if wg == 1:
T.cuda.tile.apply(
T.ptx["ld.shared.b16"],
a.view("uint16"),
AS.view(*group_shape).view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.tile.apply(
T.ptx["ld.shared.b16"],
b.view("uint16"),
BS.view(*group_shape).view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.tile.apply(
T.ptx[f"{op_type}.rn.f16"],
a.view("uint16"),
a.view("uint16"),
b.view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.tile.apply(
T.ptx["st.shared.b16"],
AS.view(*group_shape).view("uint16"),
a.view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
else:
if wid == 5:
@@ -255,26 +255,26 @@ def test_binary_op_shared_subcta_scope(exec_scope,
op_type):
T.ptx["ld.shared.b16"],
a.view("uint16"),
AS.view(*group_shape).view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.tile.apply(
T.ptx["ld.shared.b16"],
b.view("uint16"),
BS.view(*group_shape).view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.tile.apply(
T.ptx[f"{op_type}.rn.f16"],
a.view("uint16"),
a.view("uint16"),
b.view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.tile.apply(
T.ptx["st.shared.b16"],
AS.view(*group_shape).view("uint16"),
a.view("uint16"),
- scope=exec_scope,
+ scope=thread_scope,
)
T.cuda.cta_sync()
for i in T.serial(T.ceildiv(size, 256)):
@@ -289,15 +289,15 @@ def test_binary_op_shared_subcta_scope(exec_scope,
op_type):
@pytest.mark.gpu
@pytest.mark.skipif(not env.has_cuda(), reason="need cuda")
[email protected]("exec_scope", ["cta", "warpgroup", "warp"])
[email protected]("thread_scope", ["cta", "warpgroup", "warp"])
@pytest.mark.parametrize("rhs_kind", ["region", "broadcast", "const"])
@pytest.mark.parametrize("op_type", ["add", "sub", "mul", "fdiv"])
-def test_binary_op_local_subcta_trivial(exec_scope, rhs_kind, op_type):
+def test_binary_op_local_subcta_trivial(thread_scope, rhs_kind, op_type):
dtype = "float16"
m, n = 4, 8
- n_threads = 256 if exec_scope == "cta" else (128 if exec_scope ==
"warpgroup" else 32)
+ n_threads = 256 if thread_scope == "cta" else (128 if thread_scope ==
"warpgroup" else 32)
# in this test, use warp3/warpgroup1 to test
- thr_str = 0 if exec_scope == "cta" else (128 if exec_scope == "warpgroup"
else 32 * 3)
+ thr_str = 0 if thread_scope == "cta" else (128 if thread_scope ==
"warpgroup" else 32 * 3)
a_shape = (n_threads, m, n)
b_n = n if rhs_kind == "region" else 1
b_shape = (n_threads, m, b_n)
@@ -318,7 +318,7 @@ def test_binary_op_local_subcta_trivial(exec_scope,
rhs_kind, op_type):
"cta": T.cuda.linear_thread_id,
"warpgroup": T.cuda.thread_in_warpgroup,
"warp": T.cuda.lane_id,
- }[exec_scope]
+ }[thread_scope]
@T.function
def kernel(
@@ -352,12 +352,12 @@ def test_binary_op_local_subcta_trivial(exec_scope,
rhs_kind, op_type):
for j in T.serial(b_n):
B_local[i, j] = B[tid_in_scope, i, j]
- if T.constexpr(exec_scope == "cta"):
+ if T.constexpr(thread_scope == "cta"):
if T.constexpr(rhs_kind == "const"):
tx_op(C_local, A_local, const)
else:
tx_op(C_local, A_local, B_values)
- elif T.constexpr(exec_scope == "warpgroup"):
+ elif T.constexpr(thread_scope == "warpgroup"):
if wg_id == 1:
if T.constexpr(rhs_kind == "const"):
tx_op(C_local, A_local, const)
@@ -428,10 +428,10 @@ def test_binary_op_local_subcta_trivial(exec_scope,
rhs_kind, op_type):
@pytest.mark.gpu
@pytest.mark.skipif(not env.has_cuda(), reason="need cuda")
@pytest.mark.parametrize("storage_scope", ["shared", "local"])
[email protected]("exec_scope", ["cta", "thread"])
[email protected]("thread_scope", ["cta", "thread"])
@pytest.mark.parametrize("op_type", ["add", "sub", "mul", "fdiv"])
@pytest.mark.parametrize("dtype", ["float16"])
-def test_binary_op_vectorized(input, storage_scope, exec_scope, op_type,
dtype):
+def test_binary_op_vectorized(input, storage_scope, thread_scope, op_type,
dtype):
a_shape, b_shape, res_shape, thread_cnt, device_id = input
row_a, row_b = a_shape[1:], b_shape[1:]
n, m = math.prod(row_a), math.prod(row_b)
@@ -473,7 +473,7 @@ def test_binary_op_vectorized(input, storage_scope,
exec_scope, op_type, dtype):
for j in range(m):
bl[j] = bf[tid, j]
bv = b.view(*row_a, layout=broadcast)
- if T.constexpr(exec_scope == "cta"):
+ if T.constexpr(thread_scope == "cta"):
# Expose the implicit per-thread register arrays as an explicit
# CTA view. These declarations share their original storage.
av = a.view(thread_cnt // 32, 32, *row_a, layout=cta_a_layout)
diff --git a/tests/python/tirx/tile/cuda/test_cp_async_bulk.py
b/tests/python/tirx/tile/cuda/test_cp_async_bulk.py
index c8fe230613..0818d8112e 100644
--- a/tests/python/tirx/tile/cuda/test_cp_async_bulk.py
+++ b/tests/python/tirx/tile/cuda/test_cp_async_bulk.py
@@ -35,8 +35,8 @@ from tvm.ir import TensorRegion, TensorRegionType
from tvm.script import tirx as T
from tvm.testing import env
from tvm.tirx import IntImm, Var
-from tvm.tirx.exec_scope import ExecScope
from tvm.tirx.layout import S, TileLayout
+from tvm.tirx.thread_scope import ThreadScope
from tvm.tirx.tile.dispatch import DispatchContext, run_dispatch
BULK = "cp.async.bulk.shared::cluster.shared::cta.mbarrier::complete_tx::bytes"
@@ -53,7 +53,7 @@ def _make_dsmem_dispatch_call(shape, dtype, src_layout,
dst_layout, chunks=1):
T.ptx[BULK], dst, src, chunk_bytes=chunk_bytes, mbar=Var("mbar",
"handle"), remote_cta_id=1
)
target = tvm.target.Target({"kind": "cuda", "arch": "sm_90a"})
- sctx = DispatchContext(target, ExecScope("cuda", "thread"), {}, {})
+ sctx = DispatchContext(target, ThreadScope("cuda.thread"), {}, {})
return run_dispatch(op_call, sctx)
@@ -312,7 +312,7 @@ def test_dsmem_dispatch_missing_config():
buf = tvm.tirx.decl_tensor((64,), "float16", "A", scope="shared.dyn",
layout=layout)
br = TensorRegion(buf, [Range.from_min_extent(0, 64)],
ty=TensorRegionType())
target = tvm.target.Target({"kind": "cuda", "arch": "sm_90a"})
- sctx = DispatchContext(target, ExecScope("cuda", "thread"), {}, {})
+ sctx = DispatchContext(target, ThreadScope("cuda.thread"), {}, {})
with pytest.raises(RuntimeError, match="remote_cta_id"):
run_dispatch(
diff --git a/tests/python/tirx/tile/cuda/test_cp_async_bulk_tensor.py
b/tests/python/tirx/tile/cuda/test_cp_async_bulk_tensor.py
index b34207bad5..482ce496f0 100644
--- a/tests/python/tirx/tile/cuda/test_cp_async_bulk_tensor.py
+++ b/tests/python/tirx/tile/cuda/test_cp_async_bulk_tensor.py
@@ -35,8 +35,8 @@ from tvm.ir import PrimType, PtrType, Range, TensorRegion,
TensorRegionType
from tvm.script import tirx as T
from tvm.sym import Analyzer
from tvm.tirx import IntImm, Var
-from tvm.tirx.exec_scope import ExecScope
from tvm.tirx.layout import ComposeLayout, S, TileLayout
+from tvm.tirx.thread_scope import ThreadScope
from tvm.tirx.tile.dispatch import DispatchContext, run_dispatch
_TMA_OPS = {
@@ -745,7 +745,7 @@ def _region(tensor, ranges=None):
def _context(arch="sm_100a", ranges=None):
return DispatchContext(
tvm.target.Target({"kind": "cuda", "arch": arch}),
- ExecScope("cuda", "thread"),
+ ThreadScope("cuda.thread"),
{},
ranges or {},
)
diff --git a/tests/python/tirx/tile/cuda/test_mov.py
b/tests/python/tirx/tile/cuda/test_mov.py
index 01cdc4886e..6320759842 100644
--- a/tests/python/tirx/tile/cuda/test_mov.py
+++ b/tests/python/tirx/tile/cuda/test_mov.py
@@ -45,14 +45,14 @@ def _run(kernel, arrays, expected, output=0, atol=1e-3,
rtol=1e-7, target="cuda"
@pytest.mark.gpu
@pytest.mark.skipif(not env.has_cuda(), reason="need cuda")
[email protected]("exec_scope", ["warp", "warpgroup"])
-def test_unary_op_shared_subcta_scope(exec_scope):
- warps = 4 if exec_scope == "warpgroup" else 1
[email protected]("thread_scope", ["warp", "warpgroup"])
+def test_unary_op_shared_subcta_scope(thread_scope):
+ warps = 4 if thread_scope == "warpgroup" else 1
shape = (warps * 32, 8)
- group = (warps, 32, 8, 1) if exec_scope == "warpgroup" else (32, 8, 1)
+ group = (warps, 32, 8, 1) if thread_scope == "warpgroup" else (32, 8, 1)
layout = (
TileLayout(S[(4, 32, 8, 1) : (1 @ wid_in_wg, 1 @ laneid, 1, 1)])
- if exec_scope == "warpgroup"
+ if thread_scope == "warpgroup"
else TileLayout(S[(32, 8, 1) : (1 @ laneid, 1, 1)])
)
size = math.prod(shape)
@@ -71,7 +71,7 @@ def test_unary_op_shared_subcta_scope(exec_scope):
sf[q] = af[q]
T.cuda.cta_sync()
r = T.alloc_local(group, "uint16", layout=layout)
- if T.constexpr(exec_scope == "warp"):
+ if T.constexpr(thread_scope == "warp"):
if warp == 5:
T.cuda.tile.apply(T.ptx["mov.b16"], r, T.uint16(0),
scope="warp")
T.cuda.tile.apply(
@@ -97,9 +97,9 @@ def test_unary_op_shared_subcta_scope(exec_scope):
@pytest.mark.skipif(not env.has_cuda(), reason="need cuda")
@pytest.mark.parametrize("shape", [(128, 8), (128, 4, 16), (128, 5, 5)])
@pytest.mark.parametrize("op_type", ["fill"])
[email protected]("exec_scope", ["thread", "cta"])
[email protected]("thread_scope", ["thread", "cta"])
@pytest.mark.parametrize("storage_scope", ["local", "shared"])
-def test_unary_op_vectorized(shape, op_type, exec_scope, storage_scope):
+def test_unary_op_vectorized(shape, op_type, thread_scope, storage_scope):
n = math.prod(shape[1:])
bits = int(np.float16(7.89).view("uint16"))
group_layout = TileLayout(S[(4, 32, n, 1) : (1 @ warpid, 1 @ laneid, 1,
1)])
@@ -109,7 +109,7 @@ def test_unary_op_vectorized(shape, op_type, exec_scope,
storage_scope):
T.device_entry(launch=T.cuda.LaunchConfig(grid=1, block=128))
tid = T.cuda.thread_idx("x")
r = T.alloc_local(shape[1:], "float16", layout="default")
- if T.constexpr(exec_scope == "cta"):
+ if T.constexpr(thread_scope == "cta"):
values = r.view(4, 32, n, 1, layout=group_layout).view("uint16")
T.cuda.tile.apply(T.ptx["mov.b16"], values, T.uint16(bits),
scope="cta")
else:
diff --git a/tests/python/tirx/tile/test_dispatcher.py
b/tests/python/tirx/tile/test_dispatcher.py
index 660683d1db..125dd22cb3 100644
--- a/tests/python/tirx/tile/test_dispatcher.py
+++ b/tests/python/tirx/tile/test_dispatcher.py
@@ -66,12 +66,21 @@ def test_instruction_scope_contract(backend, scope):
with pytest.raises(ValueError, match=f"unsupported scope '{scope}'"):
instruction(tensor, tensor, tensor, scope=scope)
- # Raw Calls obey the same instruction contract through Op validation.
+ # Registered scope enums still obey the instruction's allowed scope set.
call = instruction(tensor, tensor, tensor)
- attrs = tvm.ir.make_node("tirx.tensor." + SPECS[call.op.name].schema,
scope=scope)
- raw = tvm.ir.Call(call.op, call.args, attrs=attrs, ty=call.ty)
- with pytest.raises(ValueError, match=f"unsupported scope '{scope}'"):
- raw.validate()
+ if scope == "kernel":
+ attrs = tvm.ir.make_node(
+ "tirx.tensor." + SPECS[call.op.name].schema,
+ scope=T.ThreadScope(f"{backend}.kernel"),
+ )
+ raw = tvm.ir.Call(call.op, call.args, attrs=attrs, ty=call.ty)
+ with pytest.raises(ValueError, match=f"unsupported scope '{scope}'"):
+ raw.validate()
+ else:
+ with pytest.raises(ValueError):
+ tvm.ir.make_node(
+ "tirx.tensor." + SPECS[call.op.name].schema,
scope=f"{backend}.{scope}"
+ )
def _custom_call(name):
@@ -81,7 +90,11 @@ def _custom_call(name):
op = Op.get(op_name)
op.set_signature(args=["token"])
op.set_attrs_type_key("tirx.tensor.ScopeAttrs")
- return Call(op, [const(7)], attrs=make_node("tirx.tensor.ScopeAttrs"))
+ return Call(
+ op,
+ [const(7)],
+ attrs=make_node("tirx.tensor.ScopeAttrs",
scope=T.ThreadScope("cuda.thread")),
+ )
def _lower(body):
@@ -101,7 +114,7 @@ def
test_op_attribute_dispatches_without_python_instruction_spec():
assert actual_call.same_as(call)
assert isinstance(context, DispatchContext)
assert context.target.kind.name == "cuda"
- assert context.exec_scope.backend == "cuda"
+ assert context.thread_scope.backend == "cuda"
assert context.scope_kind == "thread"
seen.append(actual_call)
return Function([], Evaluate(23))
diff --git a/tests/python/tirx/tile/trn/test_binary_trn.py
b/tests/python/tirx/tile/trn/test_binary_trn.py
index 286c98ed0f..60d88f9baf 100644
--- a/tests/python/tirx/tile/trn/test_binary_trn.py
+++ b/tests/python/tirx/tile/trn/test_binary_trn.py
@@ -27,7 +27,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -42,9 +42,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)
diff --git a/tests/python/tirx/tile/trn/test_compose_op_trn.py
b/tests/python/tirx/tile/trn/test_compose_op_trn.py
index 172f87581e..ac4d8f61ee 100644
--- a/tests/python/tirx/tile/trn/test_compose_op_trn.py
+++ b/tests/python/tirx/tile/trn/test_compose_op_trn.py
@@ -27,7 +27,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -42,9 +42,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)
diff --git a/tests/python/tirx/tile/trn/test_copy_trn.py
b/tests/python/tirx/tile/trn/test_copy_trn.py
index d94fcd871c..f2a21ddc03 100644
--- a/tests/python/tirx/tile/trn/test_copy_trn.py
+++ b/tests/python/tirx/tile/trn/test_copy_trn.py
@@ -31,7 +31,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -46,9 +46,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)
diff --git a/tests/python/tirx/tile/trn/test_gemm_trn.py
b/tests/python/tirx/tile/trn/test_gemm_trn.py
index 5b7f9067a7..fa6c575811 100644
--- a/tests/python/tirx/tile/trn/test_gemm_trn.py
+++ b/tests/python/tirx/tile/trn/test_gemm_trn.py
@@ -28,7 +28,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -43,9 +43,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)
diff --git a/tests/python/tirx/tile/trn/test_reduction_trn.py
b/tests/python/tirx/tile/trn/test_reduction_trn.py
index 627fab5619..582042a753 100644
--- a/tests/python/tirx/tile/trn/test_reduction_trn.py
+++ b/tests/python/tirx/tile/trn/test_reduction_trn.py
@@ -27,7 +27,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -42,9 +42,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)
diff --git a/tests/python/tirx/tile/trn/test_select_trn.py
b/tests/python/tirx/tile/trn/test_select_trn.py
index 46fc6ab4af..1e8b8e747a 100644
--- a/tests/python/tirx/tile/trn/test_select_trn.py
+++ b/tests/python/tirx/tile/trn/test_select_trn.py
@@ -27,7 +27,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -42,9 +42,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)
diff --git a/tests/python/tirx/tile/trn/test_unary_trn.py
b/tests/python/tirx/tile/trn/test_unary_trn.py
index 9f338e0db0..eb8f9d615e 100644
--- a/tests/python/tirx/tile/trn/test_unary_trn.py
+++ b/tests/python/tirx/tile/trn/test_unary_trn.py
@@ -27,7 +27,7 @@ from tvm.tirx.layout import S, TileLayout
target = tvm.target.Target("aws/trn1/trn1.2xlarge")
-def _strip_exec_scope_stmt(stmt):
+def _strip_thread_scope_stmt(stmt):
def _strip_region(node: tvm.ir.RegionStmt):
if node.op.same_as(tvm.ir.Op.get("tirx.device_entry")):
return node.body
@@ -42,9 +42,9 @@ def _strip_exec_scope_stmt(stmt):
def assert_structural_equal(lhs, rhs, *args, **kwargs):
if isinstance(lhs, tvm.tirx.Function):
- lhs = lhs.with_body(_strip_exec_scope_stmt(lhs.body))
+ lhs = lhs.with_body(_strip_thread_scope_stmt(lhs.body))
if isinstance(rhs, tvm.tirx.Function):
- rhs = rhs.with_body(_strip_exec_scope_stmt(rhs.body))
+ rhs = rhs.with_body(_strip_thread_scope_stmt(rhs.body))
_assert_structural_equal(lhs, rhs, *args, **kwargs)