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 6c76643d18 [REFACTOR][TIRx] Consolidate analysis and split-pattern 
normalization (#20629)
6c76643d18 is described below

commit 6c76643d181a1f9f83bc83a1d3e115a5187201ee
Author: Tianqi Chen <[email protected]>
AuthorDate: Fri Oct 9 12:38:52 2026 -0400

    [REFACTOR][TIRx] Consolidate analysis and split-pattern normalization 
(#20629)
    
    Keep structural undefined-variable discovery in shared IR with one
    visited-variable set. Consolidate TIRx well-formedness checks under one
    entry point and remove the separate SSA interfaces, while retaining
    S-TIR-specific block checks.
    
    Move split-pattern normalization into TIRx so it is available without
    S-TIR dependencies. Preserve compound-expression rewrites, leave
    plain-variable comparisons unchanged, and simplify the existing
    normalization fixtures to serial TIRx programs.
---
 include/tvm/ir/analysis.h                          |  13 +-
 include/tvm/s_tir/transform.h                      |   6 -
 include/tvm/tirx/analysis.h                        |  53 +-----
 include/tvm/tirx/transform.h                       |   6 +
 python/tvm/s_tir/backend/adreno/pipeline.py        |   2 +-
 python/tvm/s_tir/pipeline.py                       |   2 +-
 python/tvm/s_tir/transform/transform.py            |  11 --
 python/tvm/tirx/analysis/analysis.py               |  42 +----
 .../tvm/tirx/script/ir_builder/parser_protocol.py  |   1 -
 python/tvm/tirx/transform/transform.py             |  11 ++
 src/tirx/analysis/verify_ssa.cc                    | 150 ----------------
 src/tirx/analysis/verify_tirx_well_formed.cc       | 195 ---------------------
 src/tirx/analysis/verify_well_formed.cc            |   2 +-
 src/tirx/analysis/verify_well_formed.h             |  95 +++++++++-
 .../transform/renormalize_split_pattern.cc         |  19 +-
 tests/python/relax/test_relax_operators.py         |   2 +-
 ...st_s_tir_transform_renormalize_split_pattern.py | 176 -------------------
 .../tirx-analysis/test_tir_analysis_verify_ssa.py  |  42 -----
 .../test_tir_analysis_verify_well_formed.py        |  26 +++
 .../test_tir_transform_common_subexpr_elim.py      |   2 +-
 ...test_tir_transform_renormalize_split_pattern.py |  66 +++++++
 .../script/test_tirx_script_source_locations.py    |   2 +-
 tests/python/tirx/test_verifier.py                 |   6 +-
 23 files changed, 231 insertions(+), 699 deletions(-)

diff --git a/include/tvm/ir/analysis.h b/include/tvm/ir/analysis.h
index b40a418bbf..e79c738860 100644
--- a/include/tvm/ir/analysis.h
+++ b/include/tvm/ir/analysis.h
@@ -42,18 +42,17 @@ namespace tvm {
  * \return Undefined variables, uniquely ordered by their first use.
  */
 inline ffi::Array<Var> UndefinedVars(ffi::AnyView object, const 
ffi::Array<Var>& defs = {}) {
-  std::unordered_set<const VarNode*> defined;
-  std::unordered_set<const VarNode*> used;
-  for (const Var& var : defs) defined.insert(var.get());
+  std::unordered_set<const VarNode*> visited_vars;
+  for (const Var& var : defs) visited_vars.insert(var.get());
   ffi::Array<Var> undefined;
   ffi::StructuralWalk<ffi::WalkOrder::kPreOrder>(
-      object, [&](const Var& var, TVMFFIDefRegionKind kind) -> 
ffi::Expected<ffi::WalkResult> {
+      object, [&](const VarNode* var, TVMFFIDefRegionKind kind) -> 
ffi::Expected<ffi::WalkResult> {
         if (kind != kTVMFFIDefRegionKindNone) {
-          defined.insert(var.get());
+          visited_vars.insert(var);
           return ffi::WalkResult::Advance();
         }
-        if (!defined.count(var.get()) && used.insert(var.get()).second) {
-          undefined.push_back(var);
+        if (visited_vars.insert(var).second) {
+          undefined.push_back(ffi::GetRef<Var>(var));
         }
         return ffi::WalkResult::Skip();
       });
diff --git a/include/tvm/s_tir/transform.h b/include/tvm/s_tir/transform.h
index 1b67ce5084..cc22793fc3 100644
--- a/include/tvm/s_tir/transform.h
+++ b/include/tvm/s_tir/transform.h
@@ -251,12 +251,6 @@ TVM_DLL Pass HoistIfThenElse(tvm::ffi::String variant = 
"");
  */
 TVM_DLL Pass HoistExpression();
 
-/*!
- * \brief Renormalize the split pattern from floordiv(floormod()) to 
floormod(floordiv()).
- * \return The pass.
- */
-TVM_DLL Pass RenormalizeSplitPattern();
-
 /*!
  * \brief Detect and rewrite unsafe select that contains memory access.
  * \return The pass.
diff --git a/include/tvm/tirx/analysis.h b/include/tvm/tirx/analysis.h
index c2952b7613..4445d5227a 100644
--- a/include/tvm/tirx/analysis.h
+++ b/include/tvm/tirx/analysis.h
@@ -38,17 +38,6 @@ namespace tvm {
 
 namespace tirx {
 
-/*!
- * \brief Verifies whether the IR stmt or Expr is in SSA form.
- *  That is: each Var is defined and assigned once(in Let/For)
- *
- * \param func The function to be verified.
- * \return Whether IR is in SSA form.
- *
- * \note All passes in TIR consume and produce SSA form.
- */
-TVM_DLL bool VerifySSA(const Function& func);
-
 /*!
  * \brief Verify if memory accesses are legal for a specific target device 
type.
  *
@@ -91,6 +80,10 @@ TVM_DLL size_t CalculateWorkspaceBytes(const Function& func, 
int64_t workspace_b
  *
  * - Each variable has a single point of definition.
  *
+ * - Buffer loads have consistent source, indices, and result types.
+ *
+ * - Loop control, tile-operation categories, and scope IDs are valid.
+ *
  * Dialect statements require their dialect-specific verifier.
  *
  * \param func The Function to be verified.
@@ -129,14 +122,6 @@ namespace transform {
 using tvm::transform::Pass;
 using tvm::transform::PassContext;
 
-/*!
- * \brief Pass variant of VerifySSA.
- *
- * \returns The pass.
- * \sa tvm::tirx::VerifySSA
- */
-TVM_DLL Pass VerifySSA();
-
 /*!
  * \brief Pass variant of VerifyMemory.
  *
@@ -145,36 +130,6 @@ TVM_DLL Pass VerifySSA();
  */
 TVM_DLL Pass VerifyMemory();
 
-/*!
- * \brief Pass variant of VerifyGPUCode.
- *
- * \param constraints The dict to specify constraints to check.
- *
- * \returns The pass.
- * \sa tvm::tir::VerifyGPUCode
- */
-/******** TIRx analysis helpers ********/
-
-/*!
- * \brief Verify if the given TIRX is well-formed.
- * \param func The Function to be verified.
- * \param assert_mode The indicator if it raises an error when the function is 
not well-formed.
- * \param device_func The indicator if it is a device function.
- * \return Whether it is a well-formed TIRX function.
- */
-TVM_DLL bool VerifyTIRxWellFormed(const Function& func, bool assert_mode = 
true,
-                                  bool device_func = false);
-
-/*!
- * \brief Verify if the TIRX in the given IRMOdule is well-formed.
- * \param mod The IRModule to be verified.
- * \param assert_mode The indicator if it raises an error when the function is 
not well-formed.
- * \param device_func The indicator if it is a device function.
- * \return Whether it is a well-formed TIRX module.
- */
-TVM_DLL bool VerifyTIRxWellFormed(const IRModule& mod, bool assert_mode = true,
-                                  bool device_func = false);
-
 }  // namespace transform
 }  // namespace tirx
 }  // namespace tvm
diff --git a/include/tvm/tirx/transform.h b/include/tvm/tirx/transform.h
index 38f03f94cf..d1464701dc 100644
--- a/include/tvm/tirx/transform.h
+++ b/include/tvm/tirx/transform.h
@@ -98,6 +98,12 @@ TVM_DLL Pass UnrollLoop();
  */
 TVM_DLL Pass RemoveNoOp();
 
+/*!
+ * \brief Renormalize the split pattern from floordiv(floormod()) to 
floormod(floordiv()).
+ * \return The pass.
+ */
+TVM_DLL Pass RenormalizeSplitPattern();
+
 /*!
  * \brief Run statement-level arithmetic simplifications on the TIR Function.
  *
diff --git a/python/tvm/s_tir/backend/adreno/pipeline.py 
b/python/tvm/s_tir/backend/adreno/pipeline.py
index b7910477be..1a2decf054 100644
--- a/python/tvm/s_tir/backend/adreno/pipeline.py
+++ b/python/tvm/s_tir/backend/adreno/pipeline.py
@@ -64,7 +64,7 @@ def default_tir_pipeline(*, prepare_only=False):
             [
                 s_tir.transform.HoistIfThenElse(),
                 tirx.transform.UnrollLoop(),
-                s_tir.transform.RenormalizeSplitPattern(),
+                tirx.transform.RenormalizeSplitPattern(),
                 s_tir.transform.StmtSimplify(),
                 tirx.transform.RemoveNoOp(),
                 s_tir.transform.RewriteUnsafeSelect(),
diff --git a/python/tvm/s_tir/pipeline.py b/python/tvm/s_tir/pipeline.py
index 08cfee0c83..01d31dd7a1 100644
--- a/python/tvm/s_tir/pipeline.py
+++ b/python/tvm/s_tir/pipeline.py
@@ -64,7 +64,7 @@ def default_s_tir_pipeline(*, prepare_only=False):
             [
                 s_tir.transform.HoistIfThenElse(),
                 tirx.transform.UnrollLoop(),
-                s_tir.transform.RenormalizeSplitPattern(),
+                tirx.transform.RenormalizeSplitPattern(),
                 s_tir.transform.StmtSimplify(),
                 tirx.transform.RemoveNoOp(),
                 s_tir.transform.RewriteUnsafeSelect(),
diff --git a/python/tvm/s_tir/transform/transform.py 
b/python/tvm/s_tir/transform/transform.py
index 8bf9ff3d62..1b4fc61f48 100644
--- a/python/tvm/s_tir/transform/transform.py
+++ b/python/tvm/s_tir/transform/transform.py
@@ -290,17 +290,6 @@ def HoistExpression():
     return _ffi_api.HoistExpression()  # type: ignore
 
 
-def RenormalizeSplitPattern():
-    """Renormalize the split pattern from floordiv(floormod()) to 
floormod(floordiv())
-
-    Returns
-    -------
-    fpass : tvm.transform.Pass
-        The result pass
-    """
-    return _ffi_api.RenormalizeSplitPattern()  # type: ignore
-
-
 def RewriteUnsafeSelect():
     """Detect and rewrite unsafe select that contains memory access.
 
diff --git a/python/tvm/tirx/analysis/analysis.py 
b/python/tvm/tirx/analysis/analysis.py
index 6debad5c38..c5fa88143f 100644
--- a/python/tvm/tirx/analysis/analysis.py
+++ b/python/tvm/tirx/analysis/analysis.py
@@ -25,22 +25,6 @@ from ..function import Function
 from . import _ffi_api
 
 
-def verify_ssa(func: Function) -> bool:
-    """Verify if the func is in SSA form.
-
-    Parameters
-    ----------
-    func: tvm.tirx.Function
-        The module to be verified.
-
-    Returns
-    -------
-    result : bool
-        The result of verification.
-    """
-    return _ffi_api.verify_ssa(func)  # type: ignore
-
-
 def verify_memory(func: Function) -> bool:
     """Verify if func contains illegal host side direct memory access.
 
@@ -78,7 +62,7 @@ def undefined_vars(node: Stmt | Expr, defs: list[Var] | None 
= None) -> list[Var
 
 
 def verify_well_formed(obj: Function | IRModule, assert_mode: bool = True) -> 
bool:
-    """Verify definitions and buffer-load types in ordinary TIRX.
+    """Verify definitions, buffer-load types, loop control, tile ops, and 
scope IDs.
 
     Use ``tvm.s_tir.analysis.verify_well_formed`` for schedulable blocks.
 
@@ -96,27 +80,3 @@ def verify_well_formed(obj: Function | IRModule, 
assert_mode: bool = True) -> bo
         Whether it is a well-formed TIR function.
     """
     return _ffi_api.VerifyWellFormed(obj, assert_mode)  # type: ignore # 
pylint: disable=no-member
-
-
-def verify_tirx_well_formed(
-    obj: Function | IRModule, assert_mode: bool = True, device_func: bool = 
False
-) -> bool:
-    """Verify if the given TIRX is well-formed.
-
-    Parameters
-    ----------
-    obj: Union[tvm.tirx.Function, tvm.ir.IRModule]
-        The function or module to be verified.
-
-    assert_mode: bool
-        The indicator if it raises an error when the function is not 
well-formed.
-
-    device_func: bool
-        The indicator if it is a device function.
-
-    Returns
-    -------
-    result: bool
-        Whether it is a well-formed TIRX function.
-    """
-    return _ffi_api.VerifyTIRxWellFormed(obj, assert_mode, device_func)  # 
type: ignore # pylint: disable=no-member
diff --git a/python/tvm/tirx/script/ir_builder/parser_protocol.py 
b/python/tvm/tirx/script/ir_builder/parser_protocol.py
index 949734d98a..0369929183 100644
--- a/python/tvm/tirx/script/ir_builder/parser_protocol.py
+++ b/python/tvm/tirx/script/ir_builder/parser_protocol.py
@@ -154,7 +154,6 @@ def check_well_formed_(function: _tir.Function) -> None:
     """Validate a completed TIRx function."""
     try:
         _tir.analysis.verify_well_formed(function)
-        _tir.analysis.verify_tirx_well_formed(function)
     except Exception as error:
         raise ValueError(
             "Program is not well-formed. If this is deliberate, set "
diff --git a/python/tvm/tirx/transform/transform.py 
b/python/tvm/tirx/transform/transform.py
index d40d5d9b15..c0b5eeea23 100644
--- a/python/tvm/tirx/transform/transform.py
+++ b/python/tvm/tirx/transform/transform.py
@@ -215,6 +215,17 @@ def CommonSubexprElim():
     return _ffi_api.CommonSubexprElim()  # type: ignore
 
 
+def RenormalizeSplitPattern():
+    """Renormalize the split pattern from floordiv(floormod()) to 
floormod(floordiv())
+
+    Returns
+    -------
+    fpass : tvm.transform.Pass
+        The result pass
+    """
+    return _ffi_api.RenormalizeSplitPattern()  # type: ignore
+
+
 @_ffi.register_object("tirx.transform.StmtSimplifyConfig")
 class StmtSimplifyConfig(_ffi.Object):
     """Config for stmt simplify pass"""
diff --git a/src/tirx/analysis/verify_ssa.cc b/src/tirx/analysis/verify_ssa.cc
deleted file mode 100644
index fad6db5e65..0000000000
--- a/src/tirx/analysis/verify_ssa.cc
+++ /dev/null
@@ -1,150 +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.
- */
-
-/*!
- *  SSA related checks and pass.
- *
- *  SSA requires each varaible to be only defined once.
- * \file verify_ssa.cc
- */
-#include <tvm/ffi/cast.h>
-#include <tvm/ffi/function.h>
-#include <tvm/ffi/reflection/registry.h>
-#include <tvm/ir/prim/expr.h>
-#include <tvm/tirx/analysis.h>
-#include <tvm/tirx/stmt_functor.h>
-
-#include <unordered_map>
-#include <unordered_set>
-#include <vector>
-
-namespace tvm {
-namespace tirx {
-
-class SSAVerifier final : public StmtExprVisitor {
- public:
-  bool is_ssa_{true};
-
-  ffi::Optional<VisitInterrupt> Visit(ffi::AnyView n) final {
-    if (!is_ssa_) return std::nullopt;
-    return StmtExprVisitor::Visit(n);
-  }
-  ffi::Optional<VisitInterrupt> Visit_(const prim::LetNode* op) final {
-    // Weaker SSA condition
-    // A single var can be binded in multiple lets
-    // but they have to bind to the same value.
-    // This is used to enable cases when we reuse a single let
-    // expression to cosntruct a nested expr.
-    // (let x = 1 in x + 1) * (let x = 1 in x + 1)
-    auto it = def_map_.find(op->var);
-    if (it != def_map_.end()) {
-      if (!deep_equal_(it->second.as_or_throw<PrimExpr>(), op->value)) {
-        is_ssa_ = false;
-        return std::nullopt;
-      }
-    } else {
-      MarkDef(op->var, op->value);
-    }
-    return StmtExprVisitor::Visit_(op);
-  }
-
-  ffi::Optional<VisitInterrupt> Visit_(const BindNode* op) final {
-    MarkDef(op->var, op->value);
-    return StmtExprVisitor::Visit_(op);
-  }
-  ffi::Optional<VisitInterrupt> Visit_(const ForNode* op) final {
-    MarkDef(op->loop_var, op->loop_var);
-    return StmtExprVisitor::Visit_(op);
-  }
-
-  ffi::Optional<VisitInterrupt> Visit_(const VarNode* node) final {
-    auto var = ffi::GetRef<Var>(node);
-    if (match_scope_) {
-      MarkDef(var, var, true);
-    }
-    return StmtExprVisitor::Visit_(node);
-  }
-
-  void Run(const Function& func) {
-    for (auto param : func->params) {
-      MarkDef(param, param);
-    }
-
-    match_scope_ = true;
-    for (const Var& param : func->params) {
-      WithDefRegionKind(kTVMFFIDefRegionKindPattern, [&] { return 
Visit(param->ty); });
-    }
-    match_scope_ = false;
-    this->Visit(func->body);
-  }
-
- private:
-  void MarkDef(const Var& var, Expr value, bool allow_dup = false) {
-    if (def_map_.count(var) != 0) {
-      if (!allow_dup) {
-        is_ssa_ = false;
-        return;
-      }
-    } else {
-      def_map_.insert_or_assign(var, value);
-    }
-  }
-  // whether we are in match scope, where a var can occur multiple times.
-  bool match_scope_{false};
-  // deep equal
-  prim::ExprDeepEqual deep_equal_;
-  // def map, for let, maps to the bind value, for others maps to self.
-  std::unordered_map<Var, Expr> def_map_;
-};
-
-bool VerifySSA(const Function& func) {
-  auto visitor = ffi::make_object<SSAVerifier>();
-  visitor->Run(func);
-  return visitor->is_ssa_;
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("tirx.analysis.verify_ssa", VerifySSA);
-}
-
-namespace transform {
-
-Pass VerifySSA() {
-  auto pass_func = [=](IRModule mod, PassContext ctx) {
-    for (auto kv : mod->functions) {
-      if (auto func = kv.second.as<Function>()) {
-        TVM_FFI_CHECK(VerifySSA(func.value()), RuntimeError)
-            << "IR is not in SSA form" << func.value();
-      }
-    }
-    return mod;
-  };
-  return tvm::transform::CreateModulePass(pass_func, 0, "tirx.VerifySSA", {});
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("tirx.transform.VerifySSA", VerifySSA);
-}
-
-}  // namespace transform
-
-}  // namespace tirx
-}  // namespace tvm
diff --git a/src/tirx/analysis/verify_tirx_well_formed.cc 
b/src/tirx/analysis/verify_tirx_well_formed.cc
deleted file mode 100644
index 1f11f1b41e..0000000000
--- a/src/tirx/analysis/verify_tirx_well_formed.cc
+++ /dev/null
@@ -1,195 +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.
- */
-
-/*!
- * \file tir/analysis/verify_tirx_well_formed.cc
- * \brief Check if the TIRX program is well-formed.
- */
-
-#include <tvm/ir/op.h>
-#include <tvm/ir/prim/op.h>
-#include <tvm/runtime/logging.h>
-#include <tvm/sym/analyzer.h>
-#include <tvm/tirx/analysis.h>
-#include <tvm/tirx/exec_scope.h>
-#include <tvm/tirx/op/region.h>
-#include <tvm/tirx/op_attr_types.h>
-#include <tvm/tirx/stmt.h>
-#include <tvm/tirx/stmt_functor.h>
-
-#include <exception>
-#include <optional>
-#include <tuple>
-#include <variant>
-
-#include "../ir/tir_visitor_with_path.h"
-#include "tvm/ir/module.h"
-#include "verify_well_formed.h"
-
-namespace tvm {
-namespace tirx {
-
-class ExecScopeVerifier : public Verifier<ExecScopeVerifier> {
- public:
-  using Verifier::Verifier;
-
- private:
-  using Verifier::Visit;
-
-  void Dispatch_(const tirx::TileOpCallNode* op, ffi::reflection::AccessPath 
path) override {
-    static const auto& category_map = 
Op::GetAttrMap<tirx::TIRxOpCategory>("TIRxOpCategory");
-    Verify(category_map.get(op->op, ffi::String("")) == "tile_primitive")
-        << "TIRxError: TileOpCall at " << path << " has non-tile op " << 
op->op;
-  }
-};
-
-class ScopeIdVerifier : public Verifier<ScopeIdVerifier> {
- public:
-  using Verifier::Verifier;
-
- private:
-  using Verifier::Visit;
-
-  void Dispatch_(const RegionStmtNode* op, ffi::reflection::AccessPath path) 
override {
-    if (op->op.same_as(tirx::device_entry_op())) {
-      // Device-region marker: defs gathered from the body are verified when
-      // the region exits, with launch-param sanity enforced as ``is_root``.
-      size_t baseline = scope_id_def_.size();
-      Verifier::Dispatch_(op, path);
-      size_t total = scope_id_def_.size();
-      if (total > baseline) {
-        RunScopeIdVerify(path, baseline, /*is_root=*/true);
-      }
-      while (scope_id_def_.size() > baseline) {
-        scope_id_def_.pop_back();
-      }
-      return;
-    }
-    Verifier::Dispatch_(op, path);
-  }
-
-  void RunScopeIdVerify(ffi::reflection::AccessPath path, size_t baseline, 
bool is_root) {
-    ScopeIdDefVerifier verifier;
-    Verify(verifier.Verify(scope_id_def_, ScopeIdDefVerifier::Mode::kRelaxed))
-        << "TIRxError: Scope at " << path << " has invalid scope_id_def";
-    if (is_root) {
-      // Enforce launch-parameter sanity at the device-region root.
-      auto cta_thread_it = verifier.id_set.find(ScopeBinding::kCtaThread);
-      if (cta_thread_it != verifier.id_set.end() && 
!(*cta_thread_it).second.is_deferred()) {
-        PrimExpr ext = (*cta_thread_it).second.fused_extent();
-        if (const auto* imm = ext.as<IntImmNode>()) {
-          Verify(imm->value > 0) << "TIRxError: kernel at " << path
-                                 << " has non-positive thread count " << 
imm->value;
-          bool needs_warp_align = 
verifier.id_set.count(ScopeBinding::kCtaWarp) ||
-                                  
verifier.id_set.count(ScopeBinding::kWarpThread) ||
-                                  
verifier.id_set.count(ScopeBinding::kCtaWarpgroup) ||
-                                  
verifier.id_set.count(ScopeBinding::kWarpgroupWarp);
-          if (needs_warp_align) {
-            Verify(imm->value % 32 == 0)
-                << "TIRxError: kernel at " << path << " uses warp-granular 
bindings"
-                << " but has thread count " << imm->value << " not a multiple 
of 32";
-          }
-        }
-      }
-    }
-  }
-
-  void Dispatch_(const ScopeIdDefStmtNode* op, ffi::reflection::AccessPath 
path) override {
-    scope_id_def_.push_back(op->def);
-    Verifier::Dispatch_(op, path);
-  }
-
-  Array<ScopeIdDef> scope_id_def_;
-  sym::Analyzer ana_;
-};
-
-class LayoutVerifier : public Verifier<LayoutVerifier> {
- public:
-  using Verifier::Verifier;
-
- private:
-  using Verifier::Visit;
-};
-
-class AsyncStructsVerifier : public Verifier<AsyncStructsVerifier> {
- public:
-  using Verifier::Verifier;
-
- private:
-  using Verifier::Visit;
-};
-
-class DeviceFuncVerifier : public Verifier<DeviceFuncVerifier> {
- public:
-  using Verifier::Verifier;
-
- private:
-  using Verifier::Visit;
-};
-
-bool VerifyTIRxWellFormed(const Function& func, bool assert_mode, bool 
device_func) {
-  if (!ExecScopeVerifier::Verify(func, assert_mode)) {
-    return false;
-  }
-  if (!ScopeIdVerifier::Verify(func, assert_mode)) {
-    return false;
-  }
-  if (!LayoutVerifier::Verify(func, assert_mode)) {
-    return false;
-  }
-  if (!AsyncStructsVerifier::Verify(func, assert_mode)) {
-    return false;
-  }
-  if (device_func) {
-    if (!DeviceFuncVerifier::Verify(func, assert_mode)) {
-      return false;
-    }
-  }
-  return true;
-}
-
-bool VerifyTIRxWellFormed(const IRModule& mod, bool assert_mode, bool 
device_func) {
-  for (const auto& [gvar, base_func] : mod->functions) {
-    if (auto function = base_func.as<Function>()) {
-      bool res = VerifyTIRxWellFormed(function.value(), assert_mode, 
device_func);
-      if (!res) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-TVM_FFI_STATIC_INIT_BLOCK() {
-  namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("tirx.analysis.VerifyTIRxWellFormed",
-                        [](const ffi::ObjectRef& obj, bool assert_mode, bool 
device_func) {
-                          if (auto n = obj.as<Function>()) {
-                            return VerifyTIRxWellFormed(n.value(), 
assert_mode, device_func);
-                          } else if (auto n = obj.as<IRModule>()) {
-                            return VerifyTIRxWellFormed(n.value(), 
assert_mode, device_func);
-                          } else {
-                            LOG(FATAL) << "Expects Function or IRModule,  but 
get "
-                                       << obj->GetTypeKey() << " instead.";
-                            return false;
-                          }
-                        });
-}
-}  // namespace tirx
-}  // namespace tvm
diff --git a/src/tirx/analysis/verify_well_formed.cc 
b/src/tirx/analysis/verify_well_formed.cc
index f64c513202..2ce62ba761 100644
--- a/src/tirx/analysis/verify_well_formed.cc
+++ b/src/tirx/analysis/verify_well_formed.cc
@@ -19,7 +19,7 @@
 
 /*!
  * \file tirx/analysis/verify_well_formed.cc
- * \brief Check if schedulable tirx is well-formed.
+ * \brief Check if TIRx is well-formed.
  */
 
 #include "verify_well_formed.h"
diff --git a/src/tirx/analysis/verify_well_formed.h 
b/src/tirx/analysis/verify_well_formed.h
index 83511377d8..1547352c72 100644
--- a/src/tirx/analysis/verify_well_formed.h
+++ b/src/tirx/analysis/verify_well_formed.h
@@ -22,6 +22,10 @@
 
 #include <tvm/ir/prim/op.h>
 #include <tvm/tirx/analysis.h>
+#include <tvm/tirx/exec_scope.h>
+#include <tvm/tirx/op/region.h>
+
+#include <utility>
 
 #include "../ir/tir_visitor_with_path.h"
 namespace tvm {
@@ -301,12 +305,101 @@ class LoopControlVerifier : public 
Verifier<LoopControlVerifier<PathVisitor>, Pa
   int loop_depth_{0};
 };
 
+template <typename PathVisitor>
+class ExecScopeVerifier : public Verifier<ExecScopeVerifier<PathVisitor>, 
PathVisitor> {
+  using Verifier = tirx::Verifier<ExecScopeVerifier<PathVisitor>, PathVisitor>;
+
+ public:
+  using Verifier::Verifier;
+  using Verifier::Verify;
+
+ private:
+  using Verifier::Visit;
+
+  void Dispatch_(const tirx::TileOpCallNode* op, ffi::reflection::AccessPath 
path) override {
+    static const auto& category_map = 
Op::GetAttrMap<tirx::TIRxOpCategory>("TIRxOpCategory");
+    Verify(category_map.get(op->op, ffi::String("")) == "tile_primitive")
+        << "TIRxError: TileOpCall at " << path << " has non-tile op " << 
op->op;
+  }
+};
+
+template <typename PathVisitor>
+class ScopeIdVerifier : public Verifier<ScopeIdVerifier<PathVisitor>, 
PathVisitor> {
+  using Verifier = tirx::Verifier<ScopeIdVerifier<PathVisitor>, PathVisitor>;
+
+ public:
+  using Verifier::Verifier;
+  using Verifier::Verify;
+
+ private:
+  using Verifier::Visit;
+
+  void Visit(const Function& function, AccessPath path) override {
+    Array<ScopeIdDef> enclosing_defs = std::exchange(scope_id_def_, {});
+    Verifier::Visit(function, path);
+    scope_id_def_ = std::move(enclosing_defs);
+  }
+
+  void Dispatch_(const RegionStmtNode* op, ffi::reflection::AccessPath path) 
override {
+    if (op->op.same_as(tirx::device_entry_op())) {
+      // Device-region marker: defs gathered from the body are verified when
+      // the region exits, with launch-param sanity enforced as ``is_root``.
+      size_t baseline = scope_id_def_.size();
+      Verifier::Dispatch_(op, path);
+      size_t total = scope_id_def_.size();
+      if (total > baseline) {
+        RunScopeIdVerify(path, baseline, /*is_root=*/true);
+      }
+      while (scope_id_def_.size() > baseline) {
+        scope_id_def_.pop_back();
+      }
+      return;
+    }
+    Verifier::Dispatch_(op, path);
+  }
+
+  void RunScopeIdVerify(ffi::reflection::AccessPath path, size_t baseline, 
bool is_root) {
+    ScopeIdDefVerifier verifier;
+    Verify(verifier.Verify(scope_id_def_, ScopeIdDefVerifier::Mode::kRelaxed))
+        << "TIRxError: Scope at " << path << " has invalid scope_id_def";
+    if (is_root) {
+      // Enforce launch-parameter sanity at the device-region root.
+      auto cta_thread_it = verifier.id_set.find(ScopeBinding::kCtaThread);
+      if (cta_thread_it != verifier.id_set.end() && 
!(*cta_thread_it).second.is_deferred()) {
+        PrimExpr ext = (*cta_thread_it).second.fused_extent();
+        if (const auto* imm = ext.as<IntImmNode>()) {
+          Verify(imm->value > 0) << "TIRxError: kernel at " << path
+                                 << " has non-positive thread count " << 
imm->value;
+          bool needs_warp_align = 
verifier.id_set.count(ScopeBinding::kCtaWarp) ||
+                                  
verifier.id_set.count(ScopeBinding::kWarpThread) ||
+                                  
verifier.id_set.count(ScopeBinding::kCtaWarpgroup) ||
+                                  
verifier.id_set.count(ScopeBinding::kWarpgroupWarp);
+          if (needs_warp_align) {
+            Verify(imm->value % 32 == 0)
+                << "TIRxError: kernel at " << path << " uses warp-granular 
bindings"
+                << " but has thread count " << imm->value << " not a multiple 
of 32";
+          }
+        }
+      }
+    }
+  }
+
+  void Dispatch_(const ScopeIdDefStmtNode* op, ffi::reflection::AccessPath 
path) override {
+    scope_id_def_.push_back(op->def);
+    Verifier::Dispatch_(op, path);
+  }
+
+  Array<ScopeIdDef> scope_id_def_;
+};
+
 template <typename PathVisitor, typename NodeRef>
 bool VerifyWellFormedCommon(const NodeRef& node, bool assert_mode) {
   return UndefinedVarVerifier<PathVisitor>::Verify(node, assert_mode) &&
          UndefinedBufferVerifier<PathVisitor>::Verify(node, assert_mode) &&
          TensorLoadTypeVerifier<PathVisitor>::Verify(node, assert_mode) &&
-         LoopControlVerifier<PathVisitor>::Verify(node, assert_mode);
+         LoopControlVerifier<PathVisitor>::Verify(node, assert_mode) &&
+         ExecScopeVerifier<PathVisitor>::Verify(node, assert_mode) &&
+         ScopeIdVerifier<PathVisitor>::Verify(node, assert_mode);
 }
 }  // namespace tirx
 }  // namespace tvm
diff --git a/src/s_tir/transform/renormalize_split_pattern.cc 
b/src/tirx/transform/renormalize_split_pattern.cc
similarity index 94%
rename from src/s_tir/transform/renormalize_split_pattern.cc
rename to src/tirx/transform/renormalize_split_pattern.cc
index b275741906..3d99c314b0 100644
--- a/src/s_tir/transform/renormalize_split_pattern.cc
+++ b/src/tirx/transform/renormalize_split_pattern.cc
@@ -24,18 +24,14 @@
 #include <tvm/ffi/function.h>
 #include <tvm/ffi/reflection/registry.h>
 #include <tvm/ir/prim/op.h>
-#include <tvm/s_tir/analysis.h>
-#include <tvm/s_tir/stmt_functor.h>
-#include <tvm/s_tir/transform.h>
-#include <tvm/tirx/analysis.h>
 #include <tvm/tirx/stmt.h>
+#include <tvm/tirx/transform.h>
 
-#include "../../s_tir/ir/ir_mutator_with_analyzer.h"
 #include "../../sym/pattern_match.h"
+#include "../ir/ir_mutator_with_analyzer.h"
 
 namespace tvm {
-namespace s_tir {
-using namespace tvm::tirx;
+namespace tirx {
 
 using namespace sym;
 
@@ -170,7 +166,8 @@ class SplitPatternReNormalizer : public 
IRMutatorWithAnalyzer {
     // Pattern var match IntImm
     PVar<IntImm> c1, c2;
     // x < c2 <=> x/c2 < 1 <=> floor(x / c2) < 1
-    TRY_RECURSIVE_REWRITE_IF(x < c2, floordiv(x, c2) < 1, c2.Eval()->value > 
0);  // NOLINT
+    TRY_RECURSIVE_REWRITE_IF(x < c2, floordiv(x, c2) < 1,
+                             c2.Eval()->value > 0 && !x.Eval().as<VarNode>()); 
 // NOLINT
     return ret;
   }
 
@@ -224,15 +221,15 @@ Pass RenormalizeSplitPattern() {
                   .ValueOrUnchanged(std::move(n->body));
     return f;
   };
-  return CreateFunctionPass(pass_func, 0, "s_tir.RenormalizeSplitPattern", {});
+  return CreateFunctionPass(pass_func, 0, "tirx.RenormalizeSplitPattern", {});
 }
 
 TVM_FFI_STATIC_INIT_BLOCK() {
   namespace refl = tvm::ffi::reflection;
-  refl::GlobalDef().def("s_tir.transform.RenormalizeSplitPattern", 
RenormalizeSplitPattern);
+  refl::GlobalDef().def("tirx.transform.RenormalizeSplitPattern", 
RenormalizeSplitPattern);
 }
 
 }  // namespace transform
 
-}  // namespace s_tir
+}  // namespace tirx
 }  // namespace tvm
diff --git a/tests/python/relax/test_relax_operators.py 
b/tests/python/relax/test_relax_operators.py
index 0008ec1f64..2570b3cc6d 100644
--- a/tests/python/relax/test_relax_operators.py
+++ b/tests/python/relax/test_relax_operators.py
@@ -424,7 +424,7 @@ def test_call_tir_canonical_roundtrip(name, tuple_result, 
result_kind, namespace
         tvm.tirx.Var(f"p{i}", T.Tensor((3,), "float32"))
         for i in range(1 + output_count - int(inplace))
     ]
-    func = tvm.tirx.Function(params, [tvm.tirx.Evaluate(0)], 
ret_type=tvm.ir.TupleType([]))
+    func = tvm.tirx.Function(params, [tvm.ir.Evaluate(0)], 
ret_type=tvm.ir.TupleType([]))
     callee = relax.BlockBuilder().add_func(func, "callee")
     x = relax.Var("x", tensor_type)
     kwargs = {}
diff --git 
a/tests/python/s_tir/transform/test_s_tir_transform_renormalize_split_pattern.py
 
b/tests/python/s_tir/transform/test_s_tir_transform_renormalize_split_pattern.py
deleted file mode 100644
index 50d2e453fa..0000000000
--- 
a/tests/python/s_tir/transform/test_s_tir_transform_renormalize_split_pattern.py
+++ /dev/null
@@ -1,176 +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.
-# ruff: noqa: E501, F401
-
-import tvm
-import tvm.testing
-from tvm import s_tir
-from tvm.script import s_tir as Ts
-from tvm.script import tirx as T
-
-# fmt: off
-# pylint: 
disable=no-member,invalid-name,unused-variable,line-too-long,redefined-outer-name,redundant-keyword-arg
-
[email protected]_module
-class Before:
-    @Ts.function
-    def main(inputs: T.Tensor((1, 4, 4, 512), "float32"), weight: T.Tensor((4, 
4, 512, 256), "float32"), conv2d_transpose_nhwc: T.Tensor((1, 8, 8, 256), 
"float32")) -> None:
-        # function attr dict
-        T.func_attr({"global_symbol": "main", "tirx.noalias": True})
-        inputs_flat = T.decl_tensor([8192], dtype="float32", data=inputs.data)
-        weight_flat = T.decl_tensor([2097152], dtype="float32", 
data=weight.data)
-        conv2d_transpose_nhwc_flat = T.decl_tensor([16384], dtype="float32", 
data=conv2d_transpose_nhwc.data)
-        # var definition
-        # body
-        blockIdx_x = T.launch_thread("blockIdx.x", 64)
-        conv2d_transpose_nhwc_local = T.decl_tensor([8], "float32", 
scope="local")
-        PadInput_shared = T.decl_tensor([768], "float32", scope="shared")
-        weight_shared = T.decl_tensor([4096], "float32", scope="shared")
-        threadIdx_x = T.launch_thread("threadIdx.x", 32)
-        for i2_3_init, i1_4_init, i2_4_init in T.grid(2, 2, 2):
-            conv2d_transpose_nhwc_local[i1_4_init * 4 + i2_3_init * 2 + 
i2_4_init] = T.float32(0)
-        for i6_0 in T.serial(16):
-            for ax0_ax1_ax2_ax3_fused_0 in T.serial(24):
-                PadInput_shared[ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x] = 
T.if_then_else(128 <= ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x and 
ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x < 640 and 1 <= blockIdx_x // 32 * 2 
+ (ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x) % 128 // 32 and blockIdx_x // 32 
* 2 + (ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x) % 128 // 32 < 5, 
inputs_flat[blockIdx_x // 32 * 1024 + ax0_ax1_ax2_ax3_fused_0 * 512 + i6_0 * 32 
+ threadIdx_x - 2560], T.float32(0))
-            for ax0_ax1_ax2_ax3_fused_0 in T.serial(32):
-                weight_shared[T.ramp(ax0_ax1_ax2_ax3_fused_0 * 128 + 
threadIdx_x * 4, 1, 4)] = weight_flat[T.ramp((ax0_ax1_ax2_ax3_fused_0 * 128 + 
threadIdx_x * 4) // 256 * 131072 + i6_0 * 8192 + (ax0_ax1_ax2_ax3_fused_0 * 128 
+ threadIdx_x * 4) % 256 // 8 * 256 + blockIdx_x % 32 * 8 + threadIdx_x % 2 * 
4, 1, 4)]
-            for i6_1, i2_3, i4_2, i5_2, i6_2, i1_4, i2_4 in T.grid(4, 2, 4, 4, 
8, 2, 2):
-                conv2d_transpose_nhwc_local[i1_4 * 4 + i2_3 * 2 + i2_4] = 
conv2d_transpose_nhwc_local[i1_4 * 4 + i2_3 * 2 + i2_4] + T.if_then_else((i1_4 
+ i4_2) % 2 == 0 and (i2_4 + i5_2) % 2 == 0, PadInput_shared[threadIdx_x // 8 * 
128 + (i1_4 + i4_2) // 2 * 128 + (i2_4 + i5_2) // 2 * 32 + i2_3 * 32 + i6_1 * 8 
+ i6_2], T.float32(0)) * weight_shared[i6_1 * 64 + i6_2 * 8 + threadIdx_x % 8 + 
3840 - i5_2 * 256 - i4_2 * 1024]
-        for ax1, ax2 in T.grid(2, 4):
-            conv2d_transpose_nhwc_flat[threadIdx_x // 8 * 4096 + ax1 * 2048 + 
blockIdx_x // 32 * 1024 + ax2 * 256 + blockIdx_x % 32 * 8 + threadIdx_x % 8] = 
conv2d_transpose_nhwc_local[ax1 * 4 + ax2]
-
-
[email protected]_module
-class After:
-    @Ts.function
-    def main(inputs: T.Tensor((1, 4, 4, 512), "float32"), weight: T.Tensor((4, 
4, 512, 256), "float32"), conv2d_transpose_nhwc: T.Tensor((1, 8, 8, 256), 
"float32")) -> None:
-        # function attr dict
-        T.func_attr({"global_symbol": "main", "tirx.noalias": True})
-        inputs_flat = T.decl_tensor([8192], dtype="float32", data=inputs.data)
-        weight_flat = T.decl_tensor([2097152], dtype="float32", 
data=weight.data)
-        conv2d_transpose_nhwc_flat = T.decl_tensor([16384], dtype="float32", 
data=conv2d_transpose_nhwc.data)
-        # var definition
-        # body
-        blockIdx_x = T.launch_thread("blockIdx.x", 64)
-        conv2d_transpose_nhwc_local = T.decl_tensor([8], "float32", 
scope="local")
-        PadInput_shared = T.decl_tensor([768], "float32", scope="shared")
-        weight_shared = T.decl_tensor([4096], "float32", scope="shared")
-        threadIdx_x = T.launch_thread("threadIdx.x", 32)
-        for i2_3_init, i1_4_init, i2_4_init in T.grid(2, 2, 2):
-            conv2d_transpose_nhwc_local[i1_4_init * 4 + i2_3_init * 2 + 
i2_4_init] = T.float32(0)
-        for i6_0 in T.serial(16):
-            for ax0_ax1_ax2_ax3_fused_0 in T.serial(24):
-                PadInput_shared[ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x] = 
T.if_then_else(1 <= (ax0_ax1_ax2_ax3_fused_0 + threadIdx_x // 32) // 4 and 
(ax0_ax1_ax2_ax3_fused_0 + threadIdx_x // 32) // 20 < 1 and 1 <= blockIdx_x // 
32 * 2 + (ax0_ax1_ax2_ax3_fused_0 + threadIdx_x // 32) % 4 and (blockIdx_x // 
32 * 2 + (ax0_ax1_ax2_ax3_fused_0 + threadIdx_x // 32) % 4) // 5 < 1, 
inputs_flat[blockIdx_x // 32 * 1024 + ax0_ax1_ax2_ax3_fused_0 * 512 + i6_0 * 32 
+ threadIdx_x - 2560], T.float32(0))
-            for ax0_ax1_ax2_ax3_fused_0 in T.serial(32):
-                weight_shared[T.ramp(ax0_ax1_ax2_ax3_fused_0 * 128 + 
threadIdx_x * 4, 1, 4)] = weight_flat[T.ramp((ax0_ax1_ax2_ax3_fused_0 + 
threadIdx_x * 4 // 128) // 2 * 131072 + i6_0 * 8192 + (ax0_ax1_ax2_ax3_fused_0 
* 16 + threadIdx_x * 4 // 8) % 32 * 256 + blockIdx_x % 32 * 8 + threadIdx_x % 2 
* 4, 1, 4)]
-            for i6_1, i2_3, i4_2, i5_2, i6_2, i1_4, i2_4 in T.grid(4, 2, 4, 4, 
8, 2, 2):
-                conv2d_transpose_nhwc_local[i1_4 * 4 + i2_3 * 2 + i2_4] = 
conv2d_transpose_nhwc_local[i1_4 * 4 + i2_3 * 2 + i2_4] + T.if_then_else((i1_4 
+ i4_2) % 2 == 0 and (i2_4 + i5_2) % 2 == 0, PadInput_shared[threadIdx_x // 8 * 
128 + (i1_4 + i4_2) // 2 * 128 + (i2_4 + i5_2) // 2 * 32 + i2_3 * 32 + i6_1 * 8 
+ i6_2], T.float32(0)) * weight_shared[i6_1 * 64 + i6_2 * 8 + threadIdx_x % 8 + 
3840 - i5_2 * 256 - i4_2 * 1024]
-        for ax1, ax2 in T.grid(2, 4):
-            conv2d_transpose_nhwc_flat[threadIdx_x // 8 * 4096 + ax1 * 2048 + 
blockIdx_x // 32 * 1024 + ax2 * 256 + blockIdx_x % 32 * 8 + threadIdx_x % 8] = 
conv2d_transpose_nhwc_local[ax1 * 4 + ax2]
-
-
[email protected]_module
-class After_simplified:
-    @Ts.function
-    def main(inputs: T.Tensor((1, 4, 4, 512), "float32"), weight: T.Tensor((4, 
4, 512, 256), "float32"), conv2d_transpose_nhwc: T.Tensor((1, 8, 8, 256), 
"float32")) -> None:
-        # function attr dict
-        T.func_attr({"global_symbol": "main", "tirx.noalias": True})
-        # var definition
-        inputs_flat = T.decl_tensor([8192], dtype="float32", data=inputs.data)
-        weight_flat = T.decl_tensor([2097152], dtype="float32", 
data=weight.data)
-        conv2d_transpose_nhwc_flat = T.decl_tensor([16384], dtype="float32", 
data=conv2d_transpose_nhwc.data)
-        # body
-        blockIdx_x = T.launch_thread("blockIdx.x", 64)
-        conv2d_transpose_nhwc_local = T.decl_tensor([8], "float32", 
scope="local")
-        PadInput_shared = T.decl_tensor([768], "float32", scope="shared")
-        weight_shared = T.decl_tensor([4096], "float32", scope="shared")
-        threadIdx_x = T.launch_thread("threadIdx.x", 32)
-        for i2_3_init, i1_4_init, i2_4_init in T.grid(2, 2, 2):
-            conv2d_transpose_nhwc_local[i1_4_init * 4 + i2_3_init * 2 + 
i2_4_init] = T.float32(0)
-        for i6_0 in T.serial(16):
-            for ax0_ax1_ax2_ax3_fused_0 in T.serial(24):
-                PadInput_shared[ax0_ax1_ax2_ax3_fused_0 * 32 + threadIdx_x] = 
T.if_then_else(4 <= ax0_ax1_ax2_ax3_fused_0 and ax0_ax1_ax2_ax3_fused_0 < 20 
and 1 <= blockIdx_x // 32 * 2 + ax0_ax1_ax2_ax3_fused_0 % 4 and blockIdx_x // 
32 * 2 + ax0_ax1_ax2_ax3_fused_0 % 4 < 5, inputs_flat[blockIdx_x // 32 * 1024 + 
ax0_ax1_ax2_ax3_fused_0 * 512 + i6_0 * 32 + threadIdx_x - 2560], T.float32(0))
-            for ax0_ax1_ax2_ax3_fused_0 in T.serial(32):
-                weight_shared[T.ramp(ax0_ax1_ax2_ax3_fused_0 * 128 + 
threadIdx_x * 4, 1, 4)] = weight_flat[T.ramp(ax0_ax1_ax2_ax3_fused_0 // 2 * 
131072 + i6_0 * 8192 + ax0_ax1_ax2_ax3_fused_0 % 2 * 4096 + threadIdx_x // 2 * 
256 + blockIdx_x % 32 * 8 + threadIdx_x % 2 * 4, 1, 4)]
-            for i6_1, i2_3, i4_2, i5_2, i6_2, i1_4, i2_4 in T.grid(4, 2, 4, 4, 
8, 2, 2):
-                conv2d_transpose_nhwc_local[i1_4 * 4 + i2_3 * 2 + i2_4] = 
conv2d_transpose_nhwc_local[i1_4 * 4 + i2_3 * 2 + i2_4] + T.if_then_else((i1_4 
+ i4_2) % 2 == 0 and (i2_4 + i5_2) % 2 == 0, PadInput_shared[threadIdx_x // 8 * 
128 + (i1_4 + i4_2) // 2 * 128 + (i2_4 + i5_2) // 2 * 32 + i2_3 * 32 + i6_1 * 8 
+ i6_2], T.float32(0)) * weight_shared[i6_1 * 64 + i6_2 * 8 + threadIdx_x % 8 + 
3840 - i5_2 * 256 - i4_2 * 1024]
-        for ax1, ax2 in T.grid(2, 4):
-            conv2d_transpose_nhwc_flat[threadIdx_x // 8 * 4096 + ax1 * 2048 + 
blockIdx_x // 32 * 1024 + ax2 * 256 + blockIdx_x % 32 * 8 + threadIdx_x % 8] = 
conv2d_transpose_nhwc_local[ax1 * 4 + ax2]
-
-# pylint: 
enable=no-member,invalid-name,unused-variable,line-too-long,redefined-outer-name,redundant-keyword-arg
-# fmt: on
-
-
-def test_renormalize_split_pattern():
-    after = tvm.s_tir.transform.RenormalizeSplitPattern()(Before)
-    tvm.ir.assert_structural_equal(after, After)
-    after = tvm.tirx.transform.StmtSimplify()(after)
-    tvm.ir.assert_structural_equal(after, After_simplified)
-
-
[email protected]
-def impossible_equality(n: T.int32):
-    # Prior to bugfix, this conditional defined the expression "2" as
-    # equal to zero within the then_case. [min_value=2, max_value=0]
-    if 2 == 0:
-        # Then this expression evaluates n/2, using the min/max values
-        # of "2", which is caught as a divide by zero error.
-        if n // 2 >= 16:
-            T.evaluate(0)
-
-
[email protected]
-def impossible_inequality(n: T.int32):
-    # Prior to bugfix, this conditional set up a range of possible
-    # values for the expression "-2" as [0, kPosInf].
-    if -1 < -2:
-        if n // (-2) >= 16:
-            T.evaluate(0)
-
-
-integer_condition = tvm.testing.parameter(
-    impossible_equality,
-    impossible_inequality,
-)
-
-
-def test_analyze_inside_integer_conditional(integer_condition):
-    """Avoid crash occurring in ConstIntBoundAnalyzer.
-
-    Crash occurred when simplifying some expressions with provably
-    false integer expressions.  If the expressions were renormalized
-    before calling Simplify, conditional statements could assign a
-    range of possible values to integers, as if they were variables.
-    This would result in divide by zero throwing an exception,
-    followed by a second exception during stack unwinding causing the
-    program to crash.
-    """
-
-    # Similar issue would occur in most transformations that subclass
-    # IRMutatorWithAnalyzer.  tirx.transform.StmtSimplify() is an
-    # exception, as it rewrites the integer conditionals first.  These
-    # tests are written using RenormalizeSplitPattern as it is the
-    # first case identified.
-    transform = tvm.s_tir.transform.RenormalizeSplitPattern()
-
-    # Issue would result in an error through while applying the transformation.
-    mod = tvm.IRModule.from_expr(integer_condition)
-    transform(mod)
-
-
-if __name__ == "__main__":
-    tvm.testing.main()
diff --git a/tests/python/tirx-analysis/test_tir_analysis_verify_ssa.py 
b/tests/python/tirx-analysis/test_tir_analysis_verify_ssa.py
deleted file mode 100644
index 6b883a0c17..0000000000
--- a/tests/python/tirx-analysis/test_tir_analysis_verify_ssa.py
+++ /dev/null
@@ -1,42 +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 tvm
-
-
-def test_verify_ssa():
-    x = tvm.tirx.Var("x", "int32")
-    y = tvm.tirx.Var("tindex", "int32")
-    z = tvm.ir.Evaluate(x + y)
-    assert tvm.tirx.analysis.verify_ssa(tvm.tirx.Function([x, y], z))
-
-    assert not tvm.tirx.analysis.verify_ssa(
-        tvm.tirx.Function([x, y], tvm.ir.SeqStmt([tvm.ir.Bind(x, 1), z]))
-    )
-
-
-def test_verify_weak_let_ssa():
-    x = tvm.tirx.Var("x", "int32")
-    z1 = tvm.tirx.Let(x, 1, x + 1)
-    z2 = tvm.tirx.Let(x, 2, x + 2)
-
-    assert tvm.tirx.analysis.verify_ssa(tvm.tirx.Function([], 
tvm.ir.Evaluate(z1 + z1)))
-    assert not tvm.tirx.analysis.verify_ssa(tvm.tirx.Function([], 
tvm.ir.Evaluate(z1 * z2)))
-
-
-if __name__ == "__main__":
-    test_verify_ssa()
-    test_verify_weak_let_ssa()
diff --git a/tests/python/tirx-analysis/test_tir_analysis_verify_well_formed.py 
b/tests/python/tirx-analysis/test_tir_analysis_verify_well_formed.py
index d0d4880e19..4b8ff4a771 100644
--- a/tests/python/tirx-analysis/test_tir_analysis_verify_well_formed.py
+++ b/tests/python/tirx-analysis/test_tir_analysis_verify_well_formed.py
@@ -376,5 +376,31 @@ def 
test_tensor_load_malformed_indices_return_false_without_asserting():
         tvm.tirx.analysis.verify_well_formed(non_final_vector)
 
 
+def test_verify_unique_definitions():
+    x = tvm.tirx.Var("x", "int32")
+    y = tvm.tirx.Var("tindex", "int32")
+    z = tvm.ir.Evaluate(x + y)
+    assert tvm.tirx.analysis.verify_well_formed(tvm.tirx.Function([x, y], z))
+
+    assert not tvm.tirx.analysis.verify_well_formed(
+        tvm.tirx.Function([x, y], tvm.ir.SeqStmt([tvm.ir.Bind(x, 1), z])),
+        assert_mode=False,
+    )
+
+
+def test_verify_repeated_let_definitions():
+    x = tvm.tirx.Var("x", "int32")
+    z1 = tvm.tirx.Let(x, 1, x + 1)
+    z2 = tvm.tirx.Let(x, 2, x + 2)
+
+    # Each occurrence must define a distinct variable, even for a shared Let.
+    assert not tvm.tirx.analysis.verify_well_formed(
+        tvm.tirx.Function([], tvm.ir.Evaluate(z1 + z1)), assert_mode=False
+    )
+    assert not tvm.tirx.analysis.verify_well_formed(
+        tvm.tirx.Function([], tvm.ir.Evaluate(z1 * z2)), assert_mode=False
+    )
+
+
 if __name__ == "__main__":
     tvm.testing.main()
diff --git 
a/tests/python/tirx-transform/test_tir_transform_common_subexpr_elim.py 
b/tests/python/tirx-transform/test_tir_transform_common_subexpr_elim.py
index 5ac77f933e..133b80529c 100644
--- a/tests/python/tirx-transform/test_tir_transform_common_subexpr_elim.py
+++ b/tests/python/tirx-transform/test_tir_transform_common_subexpr_elim.py
@@ -775,7 +775,7 @@ def test_shared_subtree_stays_ssa():
     for i, va in enumerate(bound_vars):
         for vb in bound_vars[i + 1 :]:
             assert not va.same_as(vb), f"duplicate var 
definitions:\n{after.script()}"
-    assert tvm.tirx.analysis.verify_ssa(after), after.script()
+    assert tvm.tirx.analysis.verify_well_formed(after), after.script()
 
 
 if __name__ == "__main__":
diff --git 
a/tests/python/tirx-transform/test_tir_transform_renormalize_split_pattern.py 
b/tests/python/tirx-transform/test_tir_transform_renormalize_split_pattern.py
new file mode 100644
index 0000000000..ab42b7f5ed
--- /dev/null
+++ 
b/tests/python/tirx-transform/test_tir_transform_renormalize_split_pattern.py
@@ -0,0 +1,66 @@
+# 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 tvm
+import tvm.testing
+from tvm.script import tirx as T
+
+
+def test_renormalize_split_pattern():
+    @tvm.script.ir_module
+    class Before:
+        @T.function
+        def main(A: T.Tensor((768,), "int32"), B: T.Tensor((96,), "int32")):
+            for i in T.serial(24):
+                for j in T.serial(32):
+                    A[i * 32 + j] = T.if_then_else(
+                        128 <= i * 32 + j and i * 32 + j < 640,
+                        (i * 32 + j) % 128 // 32,
+                        0,
+                    )
+                B[T.ramp(i * 4, 1, 4)] = T.ramp(i * 128, 1, 4) % 256 // 8
+
+    @tvm.script.ir_module
+    class After:
+        @T.function
+        def main(A: T.Tensor((768,), "int32"), B: T.Tensor((96,), "int32")):
+            for i in T.serial(24):
+                for j in T.serial(32):
+                    A[i * 32 + j] = T.if_then_else(
+                        1 <= (i + j // 32) // 4 and (i + j // 32) // 20 < 1,
+                        (i + j // 32) % 4,
+                        0,
+                    )
+                B[T.ramp(i * 4, 1, 4)] = T.ramp(i * 128, 1, 4) // 8 % 32
+
+    @tvm.script.ir_module
+    class AfterSimplified:
+        @T.function
+        def main(A: T.Tensor((768,), "int32"), B: T.Tensor((96,), "int32")):
+            for i in T.serial(24):
+                for j in T.serial(32):
+                    A[i * 32 + j] = T.if_then_else(4 <= i and i < 20, i % 4, 0)
+                B[T.ramp(i * 4, 1, 4)] = T.broadcast(i % 2 * 16, 4)
+
+    after = tvm.tirx.transform.RenormalizeSplitPattern()(Before)
+    tvm.ir.assert_structural_equal(after, After)
+    after = tvm.tirx.transform.StmtSimplify()(after)
+    tvm.ir.assert_structural_equal(after, AfterSimplified)
+
+
+if __name__ == "__main__":
+    tvm.testing.main()
diff --git a/tests/python/tirx/script/test_tirx_script_source_locations.py 
b/tests/python/tirx/script/test_tirx_script_source_locations.py
index 8a7229bd4b..43b6a629ad 100644
--- a/tests/python/tirx/script/test_tirx_script_source_locations.py
+++ b/tests/python/tirx/script/test_tirx_script_source_locations.py
@@ -243,7 +243,7 @@ def 
test_native_view_keeps_producer_identity_name_and_span(monkeypatch):
     # A native view must preserve its producer name/span and declare its 
storage exactly once.
     from functools import wraps
 
-    from tvm import ir
+    from tvm import ir, tirx
     from tvm.script.ir_builder import base
 
     original = tirx.TensorType.view
diff --git a/tests/python/tirx/test_verifier.py 
b/tests/python/tirx/test_verifier.py
index 5bddb07737..96fc79de27 100644
--- a/tests/python/tirx/test_verifier.py
+++ b/tests/python/tirx/test_verifier.py
@@ -18,7 +18,7 @@ import pytest
 
 from tvm.script import tirx as T
 from tvm.script.tirx import tile as Tx
-from tvm.tirx.analysis import verify_tirx_well_formed as verify
+from tvm.tirx.analysis import verify_well_formed as verify
 
 
 def test_root_scope():
@@ -244,8 +244,8 @@ def test_device_func():
         T.thread_id([128])
         Tx.fill(A, 0.)
     # fmt: on
-    verify(test1, device_func=True)
-    verify(test2, device_func=True)
+    verify(test1)
+    verify(test2)
 
 
 def test_preferred_cluster_validation():

Reply via email to