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 94d8ed3434 [REFACTOR][Support] Expose compiler GPU thread-tag parsing
(#20689)
94d8ed3434 is described below
commit 94d8ed34347908811625e6ec4502e51aee24c270
Author: Tianqi Chen <[email protected]>
AuthorDate: Mon Oct 12 19:45:42 2026 -0400
[REFACTOR][Support] Expose compiler GPU thread-tag parsing (#20689)
GPU thread tags describe compiler execution axes. Expose their parser in
a small public support header that consumes ffi::String directly, and
migrate scheduling, lowering, and backend code generation to it,
retiring runtime ThreadScope.
Preserve physical-tag parsing and existing consumer checks, keep
virtual-thread handling local to compiler consumers, and leave
launch-parameter decoding independent.
---
include/tvm/support/gpu_thread_tag.h | 67 ++++++++++++++++++++++
src/backend/cuda/codegen/codegen_cuda.cc | 10 ++--
src/backend/cuda/codegen/llvm/codegen_nvptx.cc | 3 +-
src/backend/metal/codegen/codegen_metal.cc | 6 +-
src/backend/opencl/codegen/codegen_opencl.cc | 4 +-
src/backend/rocm/codegen/llvm/codegen_amdgpu.cc | 3 +-
src/backend/vulkan/codegen/codegen_spirv.cc | 6 +-
src/backend/webgpu/codegen/codegen_webgpu.cc | 10 ++--
src/runtime/thread_storage_scope.h | 45 ---------------
.../analysis/sblock_tensor_access_lca_detector.cc | 10 ++--
.../postproc/rewrite_reduction_block.cc | 3 +-
.../postproc/rewrite_unbound_block.cc | 3 +-
.../meta_schedule/postproc/verify_gpu_code.cc | 3 +-
.../meta_schedule/schedule/cuda/thread_bind.cc | 5 +-
.../schedule_rule/cross_thread_reduction.cc | 7 ++-
src/s_tir/schedule/analysis/analysis.cc | 4 +-
src/s_tir/schedule/primitive/for_kind.cc | 9 +--
src/s_tir/schedule/utils.h | 21 +++++--
src/s_tir/transform/compact_tensor_region.cc | 3 +-
src/s_tir/transform/lift_thread_binding.cc | 11 +---
src/s_tir/transform/loop_partition.cc | 24 ++++----
.../transform/lower_cross_thread_reduction.cc | 40 ++++++-------
.../transform/memhammer_intermediate_stage.cc | 3 +-
src/tirx/transform/lower_thread_allreduce.h | 10 ++--
24 files changed, 170 insertions(+), 140 deletions(-)
diff --git a/include/tvm/support/gpu_thread_tag.h
b/include/tvm/support/gpu_thread_tag.h
new file mode 100644
index 0000000000..800c6cbb27
--- /dev/null
+++ b/include/tvm/support/gpu_thread_tag.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+/*!
+ * \file tvm/support/gpu_thread_tag.h
+ * \brief Parse GPU thread tags used by compiler passes and code generation.
+ */
+#ifndef TVM_SUPPORT_GPU_THREAD_TAG_H_
+#define TVM_SUPPORT_GPU_THREAD_TAG_H_
+
+#include <tvm/ffi/error.h>
+#include <tvm/ffi/string.h>
+
+namespace tvm {
+namespace support {
+
+/*! \brief The rank and dimension of a physical GPU thread tag. */
+struct GPUThreadTag {
+ /*! \brief Block, thread, cluster CTA, or preferred cluster CTA rank (0
through 3). */
+ int rank{0};
+ /*! \brief The dimension index, relative to x. */
+ int dim_index{0};
+
+ /*!
+ * \brief Parse a physical GPU thread tag.
+ *
+ * Recognizes blockIdx., threadIdx., clusterCtaIdx., and
preferredClusterCtaIdx.
+ * prefixes. The first character after the prefix determines the dimension;
+ * trailing characters are ignored. Consumers validate supported ranks and
dimensions.
+ * \param tag The thread tag to parse.
+ * \return The parsed rank and dimension.
+ */
+ static GPUThreadTag Parse(const ffi::String& tag) {
+ if (tag.starts_with("blockIdx.")) {
+ return {0, static_cast<int>(tag.c_str()[9] - 'x')};
+ } else if (tag.starts_with("threadIdx.")) {
+ return {1, static_cast<int>(tag.c_str()[10] - 'x')};
+ } else if (tag.starts_with("clusterCtaIdx.")) {
+ return {2, static_cast<int>(tag.c_str()[14] - 'x')};
+ } else if (tag.starts_with("preferredClusterCtaIdx.")) {
+ return {3, static_cast<int>(tag.c_str()[23] - 'x')};
+ } else {
+ TVM_FFI_THROW(InternalError) << "Unknown threadscope " << tag;
+ return {};
+ }
+ }
+};
+
+} // namespace support
+} // namespace tvm
+#endif // TVM_SUPPORT_GPU_THREAD_TAG_H_
diff --git a/src/backend/cuda/codegen/codegen_cuda.cc
b/src/backend/cuda/codegen/codegen_cuda.cc
index acf8a51396..8a0961d094 100644
--- a/src/backend/cuda/codegen/codegen_cuda.cc
+++ b/src/backend/cuda/codegen/codegen_cuda.cc
@@ -28,6 +28,7 @@
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/function.h>
#include <tvm/ir/prim/op.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/sym/analyzer.h>
#include <tvm/tirx/index_map.h>
#include <tvm/tirx/op/gpu.h>
@@ -42,7 +43,6 @@
#include <utility>
#include <vector>
-#include "../../../runtime/thread_storage_scope.h"
#include "../../../target/build_common.h"
#include "cuda_fallback_module.h"
#include "literal/cuda_half_t.h"
@@ -207,9 +207,7 @@ class ThreadIdxExtractor : public tirx::StmtExprVisitor {
private:
ffi::Optional<VisitInterrupt> Visit_(const RegionStmtNode* op) final {
if (op->op.same_as(tirx::launch_thread_op())) {
- TVM_FFI_CHECK(
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) !=
0,
- ValueError)
+
TVM_FFI_CHECK(!op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread"),
ValueError)
<< "Virtual thread launches must be lowered before code generation";
ffi::String tag = op->args[0].as_or_throw<StringImm>()->value;
PrimExpr extent = op->args[1].as_or_throw<PrimExpr>();
@@ -462,10 +460,10 @@ void CodeGenCUDA::Dispatch_(const WhileNode* op) {
void CodeGenCUDA::BindThreadIndex(const PrimVar& var, const ffi::String&
thread_tag) {
TVM_FFI_ICHECK(!var_idmap_.count(var.get()));
- const auto& scope = runtime::ThreadScope::Create(thread_tag);
+ const auto& scope = support::GPUThreadTag::Parse(thread_tag);
TVM_FFI_ICHECK_GE(scope.dim_index, 0);
TVM_FFI_ICHECK_LT(scope.dim_index, 3);
- if (scope.IsClusterCtaIdx()) {
+ if (scope.rank == 2) {
const char dim = static_cast<char>('x' + scope.dim_index);
const std::string sreg = (scope.dim_index == 0 &&
cluster_cta_x_is_linear_rank_)
? "cluster_ctarank"
diff --git a/src/backend/cuda/codegen/llvm/codegen_nvptx.cc
b/src/backend/cuda/codegen/llvm/codegen_nvptx.cc
index f218ebeadf..502aa400f6 100644
--- a/src/backend/cuda/codegen/llvm/codegen_nvptx.cc
+++ b/src/backend/cuda/codegen/llvm/codegen_nvptx.cc
@@ -42,6 +42,7 @@
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <tvm/ffi/reflection/registry.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/op/gpu.h>
#include <tvm/tirx/op/memory.h>
#include <tvm/tirx/stmt.h>
@@ -149,7 +150,7 @@ class CodeGenNVPTX : public CodeGenLLVM {
// Return the thread index via intrinsics.
llvm::Value* GetThreadIndex(const PrimVar& var, const ffi::String&
thread_tag) final {
- runtime::ThreadScope ts = runtime::ThreadScope::Create(thread_tag);
+ support::GPUThreadTag ts = support::GPUThreadTag::Parse(thread_tag);
llvm::Intrinsic::ID intrin_id = llvm::Intrinsic::nvvm_read_ptx_sreg_tid_x;
if (ts.rank == 1) {
switch (ts.dim_index) {
diff --git a/src/backend/metal/codegen/codegen_metal.cc
b/src/backend/metal/codegen/codegen_metal.cc
index 4e33677960..a8115535d3 100644
--- a/src/backend/metal/codegen/codegen_metal.cc
+++ b/src/backend/metal/codegen/codegen_metal.cc
@@ -28,6 +28,7 @@
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/function.h>
#include <tvm/runtime/logging.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/analysis.h>
#include <tvm/tirx/function.h>
#include <tvm/tirx/op/memory.h>
@@ -42,7 +43,6 @@
#include <unordered_map>
#include <utility>
-#include "../../../runtime/thread_storage_scope.h"
#include "../../../target/build_common.h"
#include "metal_fallback_module.h"
@@ -189,7 +189,7 @@ void CodeGenMetal::AddFunction(const GlobalVar& gvar, const
Function& func) {
func->GetAttr<ffi::Array<ffi::String>>(tvm::tirx::attr::kKernelLaunchParams).value();
for (const auto& tag : launch_params) {
if (tag != runtime::launch_param::kUseDynamicSharedMemoryTag) {
- runtime::ThreadScope scope = runtime::ThreadScope::Create(tag);
+ support::GPUThreadTag scope = support::GPUThreadTag::Parse(tag);
work_dim = std::max(work_dim, scope.dim_index + 1);
}
}
@@ -216,7 +216,7 @@ void CodeGenMetal::AddFunction(const GlobalVar& gvar, const
Function& func) {
void CodeGenMetal::BindThreadIndex(const PrimVar& var, const ffi::String&
thread_tag) {
TVM_FFI_ICHECK(!var_idmap_.count(var.get()));
- runtime::ThreadScope ts = runtime::ThreadScope::Create(thread_tag);
+ support::GPUThreadTag ts = support::GPUThreadTag::Parse(thread_tag);
TVM_FFI_ICHECK(ts.rank == 0 || ts.rank == 1) << "Unsupported Metal thread
tag " << thread_tag;
TVM_FFI_ICHECK_GE(ts.dim_index, 0);
TVM_FFI_ICHECK_LT(ts.dim_index, 3);
diff --git a/src/backend/opencl/codegen/codegen_opencl.cc
b/src/backend/opencl/codegen/codegen_opencl.cc
index 7d040e106f..842008b1e3 100644
--- a/src/backend/opencl/codegen/codegen_opencl.cc
+++ b/src/backend/opencl/codegen/codegen_opencl.cc
@@ -26,13 +26,13 @@
#include <tvm/ffi/cast.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/function.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/op/memory.h>
#include <cmath>
#include <string>
#include <vector>
-#include "../../../runtime/thread_storage_scope.h"
#include "../../../target/build_common.h"
#include "../runtime/texture.h"
#include "opencl_fallback_module.h"
@@ -238,7 +238,7 @@ std::string CodeGenOpenCL::Finish() {
void CodeGenOpenCL::BindThreadIndex(const PrimVar& var, const ffi::String&
thread_tag) {
TVM_FFI_ICHECK(!var_idmap_.count(var.get()));
- runtime::ThreadScope ts = runtime::ThreadScope::Create(thread_tag);
+ support::GPUThreadTag ts = support::GPUThreadTag::Parse(thread_tag);
TVM_FFI_ICHECK_GE(ts.dim_index, 0);
TVM_FFI_ICHECK_LT(ts.dim_index, 3);
std::ostringstream os;
diff --git a/src/backend/rocm/codegen/llvm/codegen_amdgpu.cc
b/src/backend/rocm/codegen/llvm/codegen_amdgpu.cc
index 2a7fcc17b0..541bdaa3a3 100644
--- a/src/backend/rocm/codegen/llvm/codegen_amdgpu.cc
+++ b/src/backend/rocm/codegen/llvm/codegen_amdgpu.cc
@@ -39,6 +39,7 @@
#include <llvm/Support/raw_ostream.h>
#include <llvm/Target/TargetMachine.h>
#include <tvm/ffi/reflection/registry.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/op/memory.h>
#include <tvm/tirx/stmt.h>
#if TVM_LLVM_VERSION < 170
@@ -162,7 +163,7 @@ class CodeGenAMDGPU : public CodeGenLLVM {
// Return the thread index via intrinsics.
llvm::Value* GetThreadIndex(const PrimVar& var, const ffi::String&
thread_tag) final {
- runtime::ThreadScope ts = runtime::ThreadScope::Create(thread_tag);
+ support::GPUThreadTag ts = support::GPUThreadTag::Parse(thread_tag);
llvm::Intrinsic::ID intrin_id = llvm::Intrinsic::amdgcn_workitem_id_x;
if (ts.rank == 1) {
switch (ts.dim_index) {
diff --git a/src/backend/vulkan/codegen/codegen_spirv.cc
b/src/backend/vulkan/codegen/codegen_spirv.cc
index 70674d4292..6bdf2b8465 100644
--- a/src/backend/vulkan/codegen/codegen_spirv.cc
+++ b/src/backend/vulkan/codegen/codegen_spirv.cc
@@ -25,6 +25,7 @@
#include <tvm/ir/prim/expr.h>
#include <tvm/ir/prim/op.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/function.h>
#include <tvm/tirx/op/abi.h>
#include <tvm/tirx/op/gpu.h>
@@ -206,7 +207,7 @@ void CodeGenSPIRV::InitFuncState() {
spirv::Value CodeGenSPIRV::GetThreadIndex(const PrimVar& var, const
ffi::String& thread_tag,
const PrimExpr& extent) {
- runtime::ThreadScope ts = runtime::ThreadScope::Create(thread_tag);
+ support::GPUThreadTag ts = support::GPUThreadTag::Parse(thread_tag);
TVM_FFI_ICHECK_GE(ts.dim_index, 0);
TVM_FFI_ICHECK_LT(ts.dim_index, 3);
spirv::Value v;
@@ -1021,8 +1022,7 @@ void CodeGenSPIRV::DispatchDeclTensor(const BindNode* op,
const CallNode* buffer
void CodeGenSPIRV::Dispatch_(const RegionStmtNode* op) {
if (op->op.same_as(tirx::launch_thread_op())) {
-
TVM_FFI_CHECK(std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread",
0) != 0,
- ValueError)
+
TVM_FFI_CHECK(!op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread"),
ValueError)
<< "Virtual thread launches must be lowered before code generation";
PrimVar var = op->body_params[0].as_or_throw<PrimVar>();
PrimExpr extent = op->args[1].as_or_throw<PrimExpr>();
diff --git a/src/backend/webgpu/codegen/codegen_webgpu.cc
b/src/backend/webgpu/codegen/codegen_webgpu.cc
index 37618e86cc..cabdb1e7aa 100644
--- a/src/backend/webgpu/codegen/codegen_webgpu.cc
+++ b/src/backend/webgpu/codegen/codegen_webgpu.cc
@@ -27,6 +27,7 @@
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/function.h>
#include <tvm/ir/prim/op.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/support/io.h>
#include <tvm/tirx/analysis.h>
#include <tvm/tirx/function.h>
@@ -139,16 +140,14 @@ class WebGPUWorkgroupInfoCollector : public
StmtExprVisitor {
ffi::Optional<VisitInterrupt> Visit_(const RegionStmtNode* op) final {
if (op->op.same_as(tirx::launch_thread_op())) {
- TVM_FFI_CHECK(
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) !=
0,
- ValueError)
+
TVM_FFI_CHECK(!op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread"),
ValueError)
<< "Virtual thread launches must be lowered before code generation";
ffi::String tag = op->args[0].as_or_throw<StringImm>()->value;
PrimExpr extent = op->args[1].as_or_throw<PrimExpr>();
auto [it, inserted] = thread_extents_.emplace(tag, extent);
TVM_FFI_CHECK(inserted || analyzer_->CanProveEqual(it->second, extent),
ValueError)
<< "Conflicting launch extents for " << tag;
- runtime::ThreadScope ts = runtime::ThreadScope::Create(tag);
+ support::GPUThreadTag ts = support::GPUThreadTag::Parse(tag);
TVM_FFI_ICHECK_GE(ts.dim_index, 0);
TVM_FFI_ICHECK_LT(ts.dim_index, 3);
if (ts.rank == 1) {
@@ -365,8 +364,7 @@ runtime::FunctionInfo CodeGenWebGPU::AddFunction(const
Function& f, bool skip_re
void CodeGenWebGPU::Dispatch_(const RegionStmtNode* op) {
if (op->op.same_as(tirx::launch_thread_op())) {
-
TVM_FFI_CHECK(std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread",
0) != 0,
- ValueError)
+
TVM_FFI_CHECK(!op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread"),
ValueError)
<< "Virtual thread launches must be lowered before code generation";
PrimVar var = op->body_params[0].as_or_throw<PrimVar>();
PrimExpr extent = op->args[1].as_or_throw<PrimExpr>();
diff --git a/src/runtime/thread_storage_scope.h
b/src/runtime/thread_storage_scope.h
index 2db8f68d6f..04bf808da3 100644
--- a/src/runtime/thread_storage_scope.h
+++ b/src/runtime/thread_storage_scope.h
@@ -205,51 +205,6 @@ struct StorageScope {
}
};
-/*! \brief class to represent thread scope */
-struct ThreadScope {
- /*! \brief The rank of thread scope */
- int rank{0};
- /*! \brief the dimension index under the rank */
- int dim_index{0};
- /*!
- * \brief Create storage scope from string
- * \param s The string to be parsed.
- * \return The storage scope.
- */
- static ThreadScope Create(const std::string& s) {
- ThreadScope r;
- if (s.compare(0, 7, "vthread") == 0) {
- // virtual thread at the same level as local
- r.rank = 1;
- r.dim_index = -1;
- } else if (s.compare(0, 9, "blockIdx.") == 0) {
- r.rank = 0;
- r.dim_index = static_cast<int>(s[9] - 'x');
- } else if (s.compare(0, 10, "threadIdx.") == 0) {
- r.rank = 1;
- r.dim_index = static_cast<int>(s[10] - 'x');
- } else if (s.compare(0, 14, "clusterCtaIdx.") == 0) {
- r.rank = 2;
- r.dim_index = static_cast<int>(s[14] - 'x');
- } else if (s.compare(0, 23, "preferredClusterCtaIdx.") == 0) {
- r.rank = 3;
- r.dim_index = static_cast<int>(s[23] - 'x');
- } else {
- TVM_FFI_THROW(InternalError) << "Unknown threadscope " << s;
- }
- return r;
- }
-
- /*! \brief Whether the thread scope is a virtual thread */
- bool IsVirtualThread() const { return rank == 1 && dim_index == -1; }
- /*! \brief Whether the thread scope is a block */
- bool IsBlockIdx() const { return rank == 0; }
- /*! \brief Whether the thread scope is a thread */
- bool IsThreadIdx() const { return rank == 1 && dim_index != -1; }
- /*! \brief Whether the thread scope is a cluster */
- bool IsClusterCtaIdx() const { return rank == 2; }
-};
-
} // namespace runtime
} // namespace tvm
diff --git a/src/s_tir/analysis/sblock_tensor_access_lca_detector.cc
b/src/s_tir/analysis/sblock_tensor_access_lca_detector.cc
index bcd5c560c5..050e58e20d 100644
--- a/src/s_tir/analysis/sblock_tensor_access_lca_detector.cc
+++ b/src/s_tir/analysis/sblock_tensor_access_lca_detector.cc
@@ -29,6 +29,7 @@
#include <tvm/s_tir/analysis.h>
#include <tvm/s_tir/stmt.h>
#include <tvm/s_tir/stmt_functor.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/analysis.h>
#include <tvm/tirx/op/region.h>
@@ -102,9 +103,8 @@ class LCADetector : public s_tir::StmtExprVisitor {
auto* current_scope = arena_.make<ScopeInfo>(parent_scope, op, n);
if (tvm::s_tir::GetThreadBinding(op).has_value()) {
- const runtime::ThreadScope& scope =
-
runtime::ThreadScope::Create(tvm::s_tir::GetThreadBinding(op).value());
- if (scope.rank == 0) {
+ const ffi::String tag = tvm::s_tir::GetThreadBinding(op).value();
+ if (!tag.starts_with("vthread") &&
support::GPUThreadTag::Parse(tag).rank == 0) {
blockidx_scopes_.push_back(current_scope);
}
}
@@ -265,8 +265,8 @@ class LCADetector : public s_tir::StmtExprVisitor {
const ScopeInfo* parent_scope = ancestor_scopes_.back();
auto* current_scope = arena_.make<ScopeInfo>(parent_scope, op,
ancestor_scopes_.size());
if (op->op.same_as(tirx::launch_thread_op()) &&
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) !=
0 &&
-
runtime::ThreadScope::Create(op->args[0].as_or_throw<StringImm>()->value).rank
== 0) {
+ !op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread") &&
+
support::GPUThreadTag::Parse(op->args[0].as_or_throw<StringImm>()->value).rank
== 0) {
blockidx_scopes_.push_back(parent_scope);
}
ancestor_scopes_.push_back(current_scope);
diff --git a/src/s_tir/meta_schedule/postproc/rewrite_reduction_block.cc
b/src/s_tir/meta_schedule/postproc/rewrite_reduction_block.cc
index 48a92fbbff..7d7762cd5f 100644
--- a/src/s_tir/meta_schedule/postproc/rewrite_reduction_block.cc
+++ b/src/s_tir/meta_schedule/postproc/rewrite_reduction_block.cc
@@ -19,6 +19,7 @@
#include <tvm/ffi/extra/structural_visit.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/s_tir/stmt.h>
+#include <tvm/support/gpu_thread_tag.h>
#include "../utils.h"
@@ -54,7 +55,7 @@ struct ReductionBlockFinder : public StmtExprVisitor {
private:
ffi::Optional<VisitInterrupt> Visit_(const ForNode* loop) final {
- runtime::ThreadScope thread_scope = GetThreadScope(loop);
+ support::GPUThreadTag thread_scope = GetThreadBindingTag(loop);
if (IsThreadIdx(thread_scope) || IsBlockIdx(thread_scope)) {
thread_bound_loop_vars_.insert(loop->loop_var.get());
}
diff --git a/src/s_tir/meta_schedule/postproc/rewrite_unbound_block.cc
b/src/s_tir/meta_schedule/postproc/rewrite_unbound_block.cc
index 1955d10c41..92e7700fbb 100644
--- a/src/s_tir/meta_schedule/postproc/rewrite_unbound_block.cc
+++ b/src/s_tir/meta_schedule/postproc/rewrite_unbound_block.cc
@@ -19,6 +19,7 @@
#include <tvm/ffi/reflection/registry.h>
#include <tvm/s_tir/meta_schedule/schedule/cuda/thread_bind.h>
#include <tvm/s_tir/stmt.h>
+#include <tvm/support/gpu_thread_tag.h>
#include "../utils.h"
@@ -53,7 +54,7 @@ class UnboundBlockFinder : public StmtExprVisitor {
private:
ffi::Optional<VisitInterrupt> Visit_(const ForNode* loop) final {
- runtime::ThreadScope thread_scope = GetThreadScope(loop);
+ support::GPUThreadTag thread_scope = GetThreadBindingTag(loop);
if (IsBlockIdx(thread_scope)) {
++n_block_idx_;
} else if (IsThreadIdx(thread_scope)) {
diff --git a/src/s_tir/meta_schedule/postproc/verify_gpu_code.cc
b/src/s_tir/meta_schedule/postproc/verify_gpu_code.cc
index 98c3a83fb0..6413e8e258 100644
--- a/src/s_tir/meta_schedule/postproc/verify_gpu_code.cc
+++ b/src/s_tir/meta_schedule/postproc/verify_gpu_code.cc
@@ -21,6 +21,7 @@
#include <tvm/ir/function.h>
#include <tvm/s_tir/stmt.h>
#include <tvm/s_tir/transform.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/function.h>
#include <tvm/tirx/transform.h>
@@ -54,7 +55,7 @@ class ThreadExtentChecker : public StmtExprVisitor {
private:
ffi::Optional<VisitInterrupt> Visit_(const ForNode* loop) {
- runtime::ThreadScope thread_scope = GetThreadScope(loop);
+ support::GPUThreadTag thread_scope = GetThreadBindingTag(loop);
if (IsThreadIdx(thread_scope)) {
const auto* p_ext_imm = loop->extent.as<IntImmNode>();
if (auto p_ext = p_ext_imm ? p_ext_imm->value.as<int64_t>() :
std::nullopt;
diff --git a/src/s_tir/meta_schedule/schedule/cuda/thread_bind.cc
b/src/s_tir/meta_schedule/schedule/cuda/thread_bind.cc
index e00709a4b3..5bec958121 100644
--- a/src/s_tir/meta_schedule/schedule/cuda/thread_bind.cc
+++ b/src/s_tir/meta_schedule/schedule/cuda/thread_bind.cc
@@ -19,6 +19,7 @@
#include <tvm/ir/prim/op.h>
#include <tvm/s_tir/meta_schedule/schedule/cuda/thread_bind.h>
#include <tvm/s_tir/schedule/schedule.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <algorithm>
#include <limits>
@@ -35,7 +36,7 @@ using namespace tvm::tirx;
using s_tir::ExprRV;
using s_tir::GetLoopIterType;
using s_tir::GetLoops;
-using s_tir::GetThreadScope;
+using s_tir::GetThreadBindingTag;
using s_tir::HasBeenMultiLevelTiled;
using s_tir::IsBlockIdx;
using s_tir::IsSingleStmt;
@@ -116,7 +117,7 @@ void BindBlockThreadIdx(Schedule sch, SBlockRV block_rv, //
for (int i = 0; i < n; ++i) {
const StmtSRef& loop_sref = loops[i];
const ForNode* loop = TVM_SREF_TO_FOR(loop_sref);
- runtime::ThreadScope thread_scope = GetThreadScope(loop);
+ support::GPUThreadTag thread_scope = GetThreadBindingTag(loop);
if (IsBlockIdx(thread_scope)) {
if (i_block_idx == -1) {
i_block_idx = i;
diff --git a/src/s_tir/meta_schedule/schedule_rule/cross_thread_reduction.cc
b/src/s_tir/meta_schedule/schedule_rule/cross_thread_reduction.cc
index 396781c93a..b5da2d6dbb 100644
--- a/src/s_tir/meta_schedule/schedule_rule/cross_thread_reduction.cc
+++ b/src/s_tir/meta_schedule/schedule_rule/cross_thread_reduction.cc
@@ -18,6 +18,7 @@
*/
#include <tvm/ffi/reflection/registry.h>
#include <tvm/s_tir/stmt.h>
+#include <tvm/support/gpu_thread_tag.h>
#include "../utils.h"
@@ -91,7 +92,7 @@ class CrossThreadReductionObj : public ScheduleRuleObj {
// bound the innermost outer loop of `target_sblock` to threadIdx.
Possibly we need to split
// the loop before binding.
// - Otherwise, we search for the extent of "threadIdx.x" and use it as
the split factor.
- if (!InThreadScope(tmp_sch, target_sblock)) {
+ if (!InThreadBinding(tmp_sch, target_sblock)) {
const ffi::Array<s_tir::LoopRV>& split_res =
tmp_sch->Split(tgt_block_innermost_loop, {std::nullopt,
thread_extent});
tmp_sch->Bind(split_res[1], "threadIdx.x");
@@ -134,11 +135,11 @@ class CrossThreadReductionObj : public ScheduleRuleObj {
* \param block The block to be checked
* \return A boolean indicating whether the block is in thread scope.
*/
- bool InThreadScope(const s_tir::Schedule& sch, const s_tir::SBlockRV& block)
{
+ bool InThreadBinding(const s_tir::Schedule& sch, const s_tir::SBlockRV&
block) {
const ffi::Array<s_tir::LoopRV>& axes = sch->GetLoops(block);
for (const s_tir::LoopRV& loop_rv : axes) {
const For& loop = sch->Get(loop_rv);
- runtime::ThreadScope thread_scope = s_tir::GetThreadScope(loop.get());
+ support::GPUThreadTag thread_scope =
s_tir::GetThreadBindingTag(loop.get());
if (s_tir::IsThreadIdx(thread_scope)) {
return true;
}
diff --git a/src/s_tir/schedule/analysis/analysis.cc
b/src/s_tir/schedule/analysis/analysis.cc
index a847fa98ab..f6b5e49bcc 100644
--- a/src/s_tir/schedule/analysis/analysis.cc
+++ b/src/s_tir/schedule/analysis/analysis.cc
@@ -22,6 +22,7 @@
#include <tvm/ir/analysis.h>
#include <tvm/ir/op.h>
#include <tvm/s_tir/stmt.h>
+#include <tvm/support/gpu_thread_tag.h>
#include "../ir_comparator.h"
#include "../utils.h"
@@ -708,8 +709,7 @@ ffi::Map<Var, Range> LoopDomainOfSRefTreePath(const
StmtSRef& low_inclusive,
if (const ForNode* loop = p->StmtAs<ForNode>()) {
if (tvm::s_tir::GetThreadBinding(loop).has_value()) {
const ffi::String thread_tag =
tvm::s_tir::GetThreadBinding(loop).value();
- if (CanRelaxStorageUnderThread(extra_relax_scope,
-
runtime::ThreadScope::Create(thread_tag))) {
+ if (CanRelaxStorageUnderThread(extra_relax_scope,
ParseThreadBindingTag(thread_tag))) {
result.Set(loop->loop_var, Range::FromMinExtent(loop->min,
loop->extent));
}
}
diff --git a/src/s_tir/schedule/primitive/for_kind.cc
b/src/s_tir/schedule/primitive/for_kind.cc
index a0eb6fa2d1..3b1c9b7395 100644
--- a/src/s_tir/schedule/primitive/for_kind.cc
+++ b/src/s_tir/schedule/primitive/for_kind.cc
@@ -19,6 +19,7 @@
#include <tvm/ffi/cast.h>
#include <tvm/ffi/extra/structural_visit.h>
#include <tvm/s_tir/stmt.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/tirx/stmt.h>
#include "../utils.h"
@@ -87,7 +88,7 @@ class WrongBlockIterTypeError : public
ScheduleErrorContextObj {
*/
void CheckLoopParallelizableInBlock(const ScheduleState& self, ForKind
for_kind,
const Var& loop_var, const SBlockRealize&
block_realize,
- runtime::ThreadScope thread_scope) {
+ support::GPUThreadTag thread_scope) {
const SBlock& block = block_realize->block;
// Cond 1. The block is required to have affine bindings.
@@ -132,7 +133,7 @@ void CheckLoopParallelizableInBlock(const ScheduleState&
self, ForKind for_kind,
* the operation is not "bind"
*/
void CheckParallelizability(const ScheduleState& self, const For& loop,
ForKind for_kind,
- runtime::ThreadScope thread_scope) {
+ support::GPUThreadTag thread_scope) {
auto walk_fn = [&](const SBlockRealize& realize) ->
ffi::Expected<ffi::WalkResult> {
// If this block doesn't have corresponding StmtSRef in the schedule
state, it must be a
// block inside `tirx.init()`. We don't check the condition for such
blocks.
@@ -175,8 +176,8 @@ void ParallelizeComputation(const ScheduleState& self,
const StmtSRef& loop_sref
// Step 2. Check whether the loop can be parallelized/vectorized/bound with
regard to each
// underlying block.
CheckParallelizability(self, ffi::GetRef<For>(loop), for_kind,
- thread_axis.has_value() ?
runtime::ThreadScope::Create(thread_axis.value())
- : runtime::ThreadScope{-1,
-1});
+ thread_axis.has_value() ?
ParseThreadBindingTag(thread_axis.value())
+ : support::GPUThreadTag{-1,
-1});
// Step 3. Loop update and IR replacement
ffi::ObjectPtr<ForNode> new_loop = ffi::make_object<ForNode>(*loop);
diff --git a/src/s_tir/schedule/utils.h b/src/s_tir/schedule/utils.h
index 08adea2731..b6fe4b6342 100644
--- a/src/s_tir/schedule/utils.h
+++ b/src/s_tir/schedule/utils.h
@@ -32,6 +32,7 @@
#include <tvm/s_tir/stmt.h>
#include <tvm/s_tir/stmt_functor.h>
#include <tvm/s_tir/utils.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/sym/analyzer.h>
#include <tvm/sym/int_set.h>
#include <tvm/sym/iter_affine_map.h>
@@ -98,7 +99,7 @@ inline ffi::Array<StmtSRef> SBlockRVs2StmtSRefs(const
Schedule& sch,
* \return A boolean indicating the result
*/
inline bool CanRelaxStorageUnderThread(const runtime::StorageScope&
storage_scope,
- const runtime::ThreadScope&
thread_scope) {
+ const support::GPUThreadTag&
thread_scope) {
if (storage_scope.rank == runtime::StorageRank::kWarp) {
// for warp memory, we only relax threadIdx.x
return thread_scope.rank == 1 && thread_scope.dim_index == 0;
@@ -160,16 +161,24 @@ inline IterVar IterVarFromLoop(const For& loop,
ffi::String name, IterVarType it
PrimVar(std::move(name), loop->loop_var.ty()), iter_var_type);
}
+/*! \brief Interpret physical and virtual thread bindings for S-TIR
scheduling. */
+inline support::GPUThreadTag ParseThreadBindingTag(const ffi::String& tag) {
+ if (tag.starts_with("vthread")) {
+ return {1, -1};
+ }
+ return support::GPUThreadTag::Parse(tag);
+}
+
/*!
* \brief Get the thread scope bound to the specific loop
* \param loop The loop to be inspected
* \return The thread scope bound to the loop
*/
-inline runtime::ThreadScope GetThreadScope(const ForNode* loop) {
+inline support::GPUThreadTag GetThreadBindingTag(const ForNode* loop) {
if (tvm::s_tir::GetThreadBinding(loop).has_value()) {
- return
runtime::ThreadScope::Create(tvm::s_tir::GetThreadBinding(loop).value());
+ return ParseThreadBindingTag(tvm::s_tir::GetThreadBinding(loop).value());
}
- return runtime::ThreadScope{-1, -1};
+ return support::GPUThreadTag{-1, -1};
}
/*!
@@ -177,7 +186,7 @@ inline runtime::ThreadScope GetThreadScope(const ForNode*
loop) {
* \param thread_scope The thread scope to be checked
* \return True if the thread scope is blockIdx
*/
-inline bool IsBlockIdx(const runtime::ThreadScope& thread_scope) {
+inline bool IsBlockIdx(const support::GPUThreadTag& thread_scope) {
return thread_scope.rank == 0; // The rank of blockIdx is 0
}
@@ -186,7 +195,7 @@ inline bool IsBlockIdx(const runtime::ThreadScope&
thread_scope) {
* \param thread_scope The thread scope to be checked
* \return True if the thread scope is threadIdx
*/
-inline bool IsThreadIdx(const runtime::ThreadScope& thread_scope) {
+inline bool IsThreadIdx(const support::GPUThreadTag& thread_scope) {
return thread_scope.rank == 1 && thread_scope.dim_index >= 0;
}
diff --git a/src/s_tir/transform/compact_tensor_region.cc
b/src/s_tir/transform/compact_tensor_region.cc
index cb721587f8..f6567adafc 100644
--- a/src/s_tir/transform/compact_tensor_region.cc
+++ b/src/s_tir/transform/compact_tensor_region.cc
@@ -30,6 +30,7 @@
#include <tvm/s_tir/stmt.h>
#include <tvm/s_tir/stmt_functor.h>
#include <tvm/s_tir/transform.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/sym/int_set.h>
#include <tvm/tirx/op/memory.h>
#include <tvm/tirx/op/region.h>
@@ -459,7 +460,7 @@ class TensorAccessRegionCollector : public StmtExprVisitor {
}
// When there is warp memory
// threadIdx.x must be set to be warp index.
- return CanRelaxStorageUnderThread(scope,
runtime::ThreadScope::Create((iter->thread_tag)));
+ return CanRelaxStorageUnderThread(scope,
ParseThreadBindingTag((iter->thread_tag)));
}
/*!
diff --git a/src/s_tir/transform/lift_thread_binding.cc
b/src/s_tir/transform/lift_thread_binding.cc
index 9661860d8f..6874025352 100644
--- a/src/s_tir/transform/lift_thread_binding.cc
+++ b/src/s_tir/transform/lift_thread_binding.cc
@@ -28,7 +28,6 @@
#include <tvm/s_tir/transform.h>
#include <tvm/tirx/stmt.h>
-#include "../../runtime/thread_storage_scope.h"
#include "../../tirx/transform/ir_utils.h"
namespace tvm {
@@ -105,15 +104,7 @@ FindLoopLCA(const Stmt& root) {
sorted_thread_tags.push_back(kv.first);
}
std::sort(sorted_thread_tags.begin(), sorted_thread_tags.end(),
- [](const std::string& lhs, const std::string& rhs) {
- return lhs.size() > rhs.size();
- runtime::ThreadScope lhs_scope =
runtime::ThreadScope::Create(lhs);
- runtime::ThreadScope rhs_scope =
runtime::ThreadScope::Create(rhs);
- if (lhs_scope.rank != rhs_scope.rank) {
- return lhs_scope.rank < rhs_scope.rank;
- }
- return lhs_scope.dim_index < rhs_scope.dim_index;
- });
+ [](const std::string& lhs, const std::string& rhs) { return
lhs.size() > rhs.size(); });
for (const auto& thread_tag : sorted_thread_tags) {
Stmt lca = finder->lca[thread_tag].back();
const IterVar& iter = finder->iters.at(thread_tag);
diff --git a/src/s_tir/transform/loop_partition.cc
b/src/s_tir/transform/loop_partition.cc
index dde8221b26..23273ab176 100644
--- a/src/s_tir/transform/loop_partition.cc
+++ b/src/s_tir/transform/loop_partition.cc
@@ -32,6 +32,7 @@
#include <tvm/s_tir/stmt.h>
#include <tvm/s_tir/stmt_functor.h>
#include <tvm/s_tir/transform.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/sym/analyzer.h>
#include <tvm/sym/bound.h>
#include <tvm/tirx/analysis.h>
@@ -43,7 +44,6 @@
#include <unordered_map>
#include <unordered_set>
-#include "../../runtime/thread_storage_scope.h"
#include "../../sym/interval_set.h"
#include "ir_utils.h"
@@ -115,7 +115,7 @@ using ExpressionSet = std::unordered_set<PrimExpr,
ffi::ObjectPtrHash, ffi::Obje
static bool IsVirtualThread(const ForNode* op) {
if (!tvm::s_tir::GetThreadBinding(op).has_value()) return false;
const auto tag = tvm::s_tir::GetThreadBinding(op).value();
- return std::string(tag).rfind("vthread", 0) == 0;
+ return tag.starts_with("vthread");
}
static bool HasPartitionHint(const ForNode* op) {
@@ -171,7 +171,7 @@ class CandidateSelector final : public StmtExprVisitor {
return StmtExprVisitor::Visit_(op);
}
if (tvm::s_tir::GetThreadBinding(op).has_value() &&
-
runtime::ThreadScope::Create(tvm::s_tir::GetThreadBinding(op).value()).rank !=
0) {
+
support::GPUThreadTag::Parse(tvm::s_tir::GetThreadBinding(op).value()).rank !=
0) {
return StmtExprVisitor::Visit_(op);
}
// partition const loop when sets partition_const_loop_
@@ -190,10 +190,10 @@ class CandidateSelector final : public StmtExprVisitor {
ffi::Optional<VisitInterrupt> Visit_(const RegionStmtNode* op) final {
if (op->op.same_as(tirx::launch_thread_op()) &&
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) !=
0) {
+ !op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread")) {
Var var = op->body_params[0].as_or_throw<PrimVar>();
- runtime::ThreadScope scope =
-
runtime::ThreadScope::Create(op->args[0].as_or_throw<StringImm>()->value);
+ support::GPUThreadTag scope =
+
support::GPUThreadTag::Parse(op->args[0].as_or_throw<StringImm>()->value);
PrimExpr extent = op->args[1].as_or_throw<PrimExpr>();
if ((scope.rank == 0) && (!IsConstInt(extent) || partition_const_loop_))
{
record_.insert({var.get(), false});
@@ -307,7 +307,7 @@ class PartitionFinder : public StmtExprVisitor {
ffi::Optional<VisitInterrupt> Visit_(const RegionStmtNode* op) final {
// handle thread_axis
if (op->op.same_as(tirx::launch_thread_op()) &&
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) !=
0) {
+ !op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread")) {
const VarNode* var = op->body_params[0].as_or_throw<PrimVar>().get();
PrimExpr extent = op->args[1].as_or_throw<PrimExpr>();
IntSet dom = IntSet::FromRange(Range(IntImm(extent.ty(), 0), extent));
@@ -476,7 +476,7 @@ class ThreadPartitionInserter : public StmtExprMutator {
UnchangedOr<Stmt> Mutate_(const RegionStmtNode* op, InplaceMode
inplace_mode) final {
if (op->op.same_as(tirx::launch_thread_op()) &&
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) !=
0) {
+ !op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread")) {
innermost_thread_scope_ = true;
Stmt stmt =
StmtExprMutator::Mutate_(op,
inplace_mode).ValueOrUnchanged(ffi::GetRef<Stmt>(op));
@@ -537,7 +537,7 @@ class LoopPartitioner : public StmtExprMutator {
// Relax threadIdx ranges to avoid introducing divergent partition
branches.
bool relax = thread_scope &&
-
runtime::ThreadScope::Create(tvm::s_tir::GetThreadBinding(op).value()).rank ==
1;
+
support::GPUThreadTag::Parse(tvm::s_tir::GetThreadBinding(op).value()).rank ==
1;
auto& ranges = relax ? relax_map_ : hint_map_;
ranges.insert({op->loop_var.get(), IntSet::Interval(op->min, op->min +
op->extent - 1)});
Stmt res = StmtExprMutator::Mutate_(op, InplaceMode::kDisallow)
@@ -548,7 +548,7 @@ class LoopPartitioner : public StmtExprMutator {
UnchangedOr<Stmt> Mutate_(const RegionStmtNode* op, InplaceMode
inplace_mode) final {
if (!op->op.same_as(tirx::launch_thread_op()) ||
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) ==
0) {
+ op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread")) {
return StmtExprMutator::Mutate_(op, inplace_mode);
}
@@ -561,8 +561,8 @@ class LoopPartitioner : public StmtExprMutator {
}
// normal path when loop parittion fails.
- runtime::ThreadScope scope =
-
runtime::ThreadScope::Create(op->args[0].as_or_throw<StringImm>()->value);
+ support::GPUThreadTag scope =
+
support::GPUThreadTag::Parse(op->args[0].as_or_throw<StringImm>()->value);
Stmt res{ffi::UnsafeInit{}};
if (scope.rank == 1) {
// threadIdx should be put into relax map, in case of divergence.
diff --git a/src/s_tir/transform/lower_cross_thread_reduction.cc
b/src/s_tir/transform/lower_cross_thread_reduction.cc
index d8b8a5301e..e32d5fd3d3 100644
--- a/src/s_tir/transform/lower_cross_thread_reduction.cc
+++ b/src/s_tir/transform/lower_cross_thread_reduction.cc
@@ -30,35 +30,36 @@
#include <tvm/s_tir/stmt.h>
#include <tvm/s_tir/stmt_functor.h>
#include <tvm/s_tir/transform.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/sym/analyzer.h>
#include <tvm/te/operation.h>
#include <tvm/tirx/analysis.h>
#include <tvm/tirx/op/gpu.h>
#include <tvm/tirx/stmt.h>
-#include "../../runtime/thread_storage_scope.h"
#include "../../support/utils.h"
#include "../../tirx/transform/ir_utils.h"
#include "../schedule/analysis.h"
+#include "../schedule/utils.h"
namespace tvm {
namespace s_tir {
using namespace tvm::prim;
using namespace tvm::tirx;
-using runtime::ThreadScope;
+using support::GPUThreadTag;
using support::StartsWith;
-// Implement a hash and equality function for ThreadScope so that
-// ThreadScope can serve as map key class
-struct ThreadScopeHash {
- size_t operator()(const ThreadScope& scope) const {
+// Implement a hash and equality function for GPUThreadTag so that
+// GPUThreadTag can serve as map key class
+struct GPUThreadTagHash {
+ size_t operator()(const GPUThreadTag& scope) const {
return static_cast<size_t>(scope.rank * 30 + scope.dim_index);
}
};
-struct ThreadScopeEqual {
- bool operator()(const ThreadScope& a, const ThreadScope& b) const {
+struct GPUThreadTagEqual {
+ bool operator()(const GPUThreadTag& a, const GPUThreadTag& b) const {
return a.rank == b.rank && a.dim_index == b.dim_index;
}
};
@@ -72,8 +73,7 @@ bool IsBoundToThreadIdx(const ForNode* loop) {
if (!tvm::s_tir::GetThreadBinding(loop).has_value()) {
return false;
}
- runtime::ThreadScope scope =
- runtime::ThreadScope::Create(tvm::s_tir::GetThreadBinding(loop).value());
+ support::GPUThreadTag scope =
ParseThreadBindingTag(tvm::s_tir::GetThreadBinding(loop).value());
return scope.rank == 1 && scope.dim_index >= 0;
}
@@ -646,7 +646,7 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
// 2. it writes to non-local memory,
// 3. at least one of the reduction thread vars of the cross-thread reduction
// is free to this block (i.e., not bound to the block).
- std::vector<std::pair<ThreadScope, Range>> NeedCrossThreadBroadcast(
+ std::vector<std::pair<GPUThreadTag, Range>> NeedCrossThreadBroadcast(
const SBlockRealizeNode* realize) {
SBlock block = realize->block;
@@ -659,7 +659,7 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
// Find out the reduction threads for the read-tensors which are produced
by
// cross-thread reduction.
- std::unordered_map<ThreadScope, Range, ThreadScopeHash, ThreadScopeEqual>
thread2range;
+ std::unordered_map<GPUThreadTag, Range, GPUThreadTagHash,
GPUThreadTagEqual> thread2range;
for (TensorRegion read_region : block->reads) {
auto tensor_it =
crt_tensor2threads_.find(read_region->source.as_or_throw<tvm::tirx::TensorVar>().get());
@@ -674,11 +674,11 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
// Erase those threads which are not free to this block.
for (const ForNode* loop : loop_stack_) {
if (tvm::s_tir::GetThreadBinding(loop).has_value()) {
- ThreadScope scope =
ThreadScope::Create(tvm::s_tir::GetThreadBinding(loop).value());
+ GPUThreadTag scope =
ParseThreadBindingTag(tvm::s_tir::GetThreadBinding(loop).value());
thread2range.erase(scope);
}
}
- std::vector<std::pair<ThreadScope, Range>> unbound_thread2range_list;
+ std::vector<std::pair<GPUThreadTag, Range>> unbound_thread2range_list;
for (auto [scope, range] : thread2range) {
unbound_thread2range_list.emplace_back(scope, range);
}
@@ -812,7 +812,7 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
bool is_block_idx = false;
bool is_thread_idx = false;
if (tvm::s_tir::GetThreadBinding(loop).has_value()) {
- ThreadScope scope =
ThreadScope::Create(tvm::s_tir::GetThreadBinding(loop).value());
+ GPUThreadTag scope =
ParseThreadBindingTag(tvm::s_tir::GetThreadBinding(loop).value());
if (scope.rank == 1 && scope.dim_index >= 0) {
is_thread_idx = true;
++thread_idx_depth;
@@ -906,12 +906,12 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
// Step 5. Record the reduction thread dims for the write-back tensors.
// The information is used for consumer block broadcasting detection.
- std::vector<std::pair<ThreadScope, Range>> reduction_threads;
+ std::vector<std::pair<GPUThreadTag, Range>> reduction_threads;
reduction_threads.reserve(reduction_loops.size());
for (const ForNode* loop : reduction_loops) {
if (tvm::s_tir::GetThreadBinding(loop).has_value()) {
reduction_threads.emplace_back(
- ThreadScope::Create(tvm::s_tir::GetThreadBinding(loop).value()),
+ ParseThreadBindingTag(tvm::s_tir::GetThreadBinding(loop).value()),
Range::FromMinExtent(loop->min, loop->extent));
}
}
@@ -922,7 +922,7 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
Stmt MakeCrossThreadBroadcast(
const SBlockRealizeNode* realize,
- const std::vector<std::pair<ThreadScope, Range>>& unbound_thread2range) {
+ const std::vector<std::pair<GPUThreadTag, Range>>& unbound_thread2range)
{
// Step 1. Generate loop var for each unbound thread.
// Update the block predicate with clauses of `thread_var == min`.
PrimExpr predicate = realize->predicate;
@@ -973,7 +973,7 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
// Part 2. Check if the block needs all-thread broadcasting rewrite.
// We only check this when cross-thread reduction was detected.
- std::vector<std::pair<ThreadScope, Range>> unbound_thread2range =
+ std::vector<std::pair<GPUThreadTag, Range>> unbound_thread2range =
NeedCrossThreadBroadcast(realize);
if (!unbound_thread2range.empty()) {
return MakeCrossThreadBroadcast(realize, unbound_thread2range);
@@ -993,7 +993,7 @@ class CrossThreadReductionTransformer : public
StmtExprMutator {
int block_idx_depth = 0;
int thread_idx_depth = 0;
- std::unordered_map<const VarNode*, std::vector<std::pair<ThreadScope,
Range>>>
+ std::unordered_map<const VarNode*, std::vector<std::pair<GPUThreadTag,
Range>>>
crt_tensor2threads_;
};
diff --git a/src/s_tir/transform/memhammer_intermediate_stage.cc
b/src/s_tir/transform/memhammer_intermediate_stage.cc
index 9e95051a46..4218623606 100644
--- a/src/s_tir/transform/memhammer_intermediate_stage.cc
+++ b/src/s_tir/transform/memhammer_intermediate_stage.cc
@@ -19,6 +19,7 @@
#include <tvm/ffi/cast.h>
#include <tvm/ffi/extra/structural_mutate.h>
#include <tvm/ffi/extra/structural_visit.h>
+#include <tvm/support/gpu_thread_tag.h>
#include "memhammer_rewrite_rule.h"
@@ -277,7 +278,7 @@ std::pair<Stmt, SeqStmt> InsertCacheStage(Stmt stmt, bool
is_write_cache, ffi::S
for (const For& loop : outer_loops) {
if (tvm::s_tir::GetThreadBinding(loop).has_value()) {
const ffi::String thread_tag =
tvm::s_tir::GetThreadBinding(loop).value();
- auto thread_scope = runtime::ThreadScope::Create(thread_tag);
+ auto thread_scope = ParseThreadBindingTag(thread_tag);
if
(CanRelaxStorageUnderThread(runtime::StorageScope::Create(storage_scope),
thread_scope)) {
if (is_write_cache && thread_scope.dim_index == 0) {
// writing C_reindex_m16n8k8_matrixC_shared_dyn is warp execution
diff --git a/src/tirx/transform/lower_thread_allreduce.h
b/src/tirx/transform/lower_thread_allreduce.h
index 122d42c2e1..069a0755af 100644
--- a/src/tirx/transform/lower_thread_allreduce.h
+++ b/src/tirx/transform/lower_thread_allreduce.h
@@ -26,6 +26,7 @@
#include <tvm/ir/function.h>
#include <tvm/ir/prim/expr.h>
#include <tvm/ir/prim/op.h>
+#include <tvm/support/gpu_thread_tag.h>
#include <tvm/sym/analyzer.h>
#include <tvm/target/target.h>
#include <tvm/tirx/op/gpu.h>
@@ -38,7 +39,6 @@
#include <map>
#include <unordered_set>
-#include "../../runtime/thread_storage_scope.h"
#include "ir_utils.h"
#include "update_pointer_storage_scope.h"
@@ -78,7 +78,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
UnchangedOr<Stmt> Mutate_(const RegionStmtNode* op, InplaceMode
inplace_mode) final {
if (!op->op.same_as(tirx::launch_thread_op()) ||
-
std::string(op->args[0].as_or_throw<StringImm>()->value).rfind("vthread", 0) ==
0) {
+ op->args[0].as_or_throw<StringImm>()->value.starts_with("vthread")) {
return DialectMutator::Mutate_(op, inplace_mode);
}
thread_extents_.push_back(op);
@@ -207,7 +207,7 @@ class ThreadAllreduceBuilder final : public DialectMutator {
private:
// Thread entry
struct ThreadEntry {
- runtime::ThreadScope scope;
+ support::GPUThreadTag scope;
ffi::Optional<PrimVar> var;
int extent;
// comparator
@@ -288,7 +288,9 @@ class ThreadAllreduceBuilder final : public DialectMutator {
ThreadEntry e;
PrimVar var = launch->body_params[0].as_or_throw<PrimVar>();
ffi::String tag = launch->args[0].as_or_throw<StringImm>()->value;
- e.scope = runtime::ThreadScope::Create(tag);
+ TVM_FFI_ICHECK(!tag.starts_with("vthread"))
+ << "vthread do not work with cross thread reduction";
+ e.scope = support::GPUThreadTag::Parse(tag);
e.var = var;
TVM_FFI_ICHECK_LE(e.scope.rank, 1);
TVM_FFI_ICHECK_GE(e.scope.dim_index, 0) << "vthread do not work with
cross thread reduction";