This is an automated email from the ASF dual-hosted git repository.

syfeng pushed a commit to branch unity
in repository https://gitbox.apache.org/repos/asf/tvm.git


The following commit(s) were added to refs/heads/unity by this push:
     new 2dcb8716e8 [Unity][BlockBuilder] Depracate `BlockBuilder.get()` and 
change it to `BlockBuilder.finalize()` (#16090)
2dcb8716e8 is described below

commit 2dcb8716e8f391e3bb395f6942bdc5a422adbab3
Author: Yixin Dong <[email protected]>
AuthorDate: Mon Nov 27 18:41:46 2023 -0800

    [Unity][BlockBuilder] Depracate `BlockBuilder.get()` and change it to 
`BlockBuilder.finalize()` (#16090)
    
    * 1108
    
    * fix ci
    
    * 1109
    
    * finished
    
    * fix ci
---
 include/tvm/relax/block_builder.h                  |   9 ++
 include/tvm/relax/transform.h                      |   9 ++
 python/tvm/relax/block_builder.py                  |  46 +++++----
 python/tvm/relax/ir/instrument.py                  |   2 +-
 python/tvm/relax/transform/__init__.py             |   1 +
 python/tvm/relax/transform/transform.py            |  15 +++
 src/relax/analysis/well_formed.cc                  |   5 +-
 src/relax/ir/block_builder.cc                      |   6 ++
 src/relax/transform/normalize.cc                   | 103 +++++++++++++++++++++
 tests/python/relax/test_blockbuilder_core.py       | 103 +++++++++++++++------
 .../relax/test_transform_normalize_global_var.py   |  98 ++++++++++++++++++++
 11 files changed, 350 insertions(+), 47 deletions(-)

diff --git a/include/tvm/relax/block_builder.h 
b/include/tvm/relax/block_builder.h
index 9b339babb4..4272b3f75e 100644
--- a/include/tvm/relax/block_builder.h
+++ b/include/tvm/relax/block_builder.h
@@ -82,6 +82,15 @@ class BlockBuilderNode : public Object {
    */
   virtual IRModule GetContextIRModule() const = 0;
 
+  /*!
+   * \brief Finalize the building process and return the result IRModule. 
Possibly rename
+   * GlobalVars in the IRModule to ensure name uniqueness and the invariant:
+   * every public function has the same name as its "global_symbol" attribute.
+   *
+   * \return The IRModule in this BlockBuilder.
+   */
+  virtual IRModule Finalize() = 0;
+
   /*!
    * \brief Add a Relax function or a TIR PrimFunc to internal context module.
    * \param func The function to be added.
diff --git a/include/tvm/relax/transform.h b/include/tvm/relax/transform.h
index f99d74b7aa..ae6d4059a6 100644
--- a/include/tvm/relax/transform.h
+++ b/include/tvm/relax/transform.h
@@ -154,6 +154,15 @@ TVM_DLL Pass AttachGlobalSymbol();
  */
 TVM_DLL Pass Normalize();
 
+/*!
+ * \brief Possibly rename the GlobalVar in an IRModule to ensure these 
properties:
+ * 1. (Invariant) First ensure every public function has the same name as its 
"global_symbol"
+ *    attribute;
+ * 2. To ensure 1., we may need to rename private functions with conflicting 
names;
+ * 3. Finally, the name of every GlobalVar is unique in the IRModule.
+ */
+TVM_DLL Pass NormalizeGlobalVar();
+
 /*!
  * \brief Simplify a Relax module by folding var bindings and match shape 
nodes,
  * as well as tuple indices.
diff --git a/python/tvm/relax/block_builder.py 
b/python/tvm/relax/block_builder.py
index 5bf36ce330..ab807203a3 100644
--- a/python/tvm/relax/block_builder.py
+++ b/python/tvm/relax/block_builder.py
@@ -14,26 +14,22 @@
 # KIND, either express or implied.  See the License for the
 # specific language governing permissions and limitations
 # under the License.
-# pylint: disable=no-else-return, invalid-name, unused-argument
+# pylint: disable=no-else-return, invalid-name, unused-argument, 
import-outside-toplevel
 """Developer API of constructing Relax AST."""
 
-from typing import Dict, List, Optional, Union, Any, Callable, Sequence
+from typing import Any, Callable, Dict, List, Optional, Sequence, Union
+
+import tvm
+from tvm import relax as rx
+from tvm import tir
+from tvm.ir.base import deprecated
 from tvm.ir.module import IRModule
 from tvm.runtime import Object
-from tvm import relax as rx, tir
-import tvm
-from .expr import (
-    Expr,
-    Var,
-    GlobalVar,
-    BindingBlock,
-    Tuple,
-    BaseFunc,
-    Binding,
-)
-from .struct_info import StructInfo
-from .op.base import call_tir, call_tir_with_grad
+
 from . import _ffi_api
+from .expr import BaseFunc, Binding, BindingBlock, Expr, GlobalVar, Tuple, Var
+from .op.base import call_tir, call_tir_with_grad
+from .struct_info import StructInfo
 from .utils import gen_call_tir_inputs
 
 
@@ -658,6 +654,7 @@ class BlockBuilder(Object):
         """
         return _ffi_api.BlockBuilderNormalize(self, expr)  # type: ignore
 
+    @deprecated("tvm.relax.BlockBuilder.get", 
"tvm.relax.BlockBuilder.finalize")
     def get(self) -> tvm.IRModule:
         """Return the IRModule being built.
 
@@ -666,7 +663,24 @@ class BlockBuilder(Object):
         ret : tvm.IRModule
             An IRModule with Relax and TIR functions being built.
         """
-        return _ffi_api.BlockBuilderGetContextIRModule(self)  # type: ignore
+        return self.finalize()
+
+    def finalize(self) -> tvm.IRModule:
+        """Finalize the building process and return the result IRModule.
+
+        Possibly rename GlobalVars in the IRModule to ensure name uniqueness 
and the invariant:
+        every public function has the same name as its "global_symbol" 
attribute.
+
+        Note this call may invalidate global vars previously returned by this 
builder
+        (see tvm.relax.transform.NormalizeGlobalVar), so it can only be called 
once at the end of
+        the building process.
+
+        Returns
+        -------
+        ret : tvm.IRModule
+            An IRModule with Relax and TIR functions being built.
+        """
+        return _ffi_api.BlockBuilderFinalize(self)  # type: ignore
 
     def get_unique_name(self, name_prefix: str) -> str:
         """Generate a unique name with a specified prefix.
diff --git a/python/tvm/relax/ir/instrument.py 
b/python/tvm/relax/ir/instrument.py
index 1ecd87fe1b..281a8af511 100644
--- a/python/tvm/relax/ir/instrument.py
+++ b/python/tvm/relax/ir/instrument.py
@@ -39,7 +39,7 @@ class WellFormedInstrument:
     """
 
     def __init__(self, check_struct_info: bool = True, 
validate_before_transform: bool = True):
-        self.skip_pass_name = ["Normalize", "ResolveGlobals"]
+        self.skip_pass_name = ["Normalize", "NormalizeGlobalVar", 
"ResolveGlobals"]
         self.check_struct_info = check_struct_info
         self.validate_before_transform = validate_before_transform
 
diff --git a/python/tvm/relax/transform/__init__.py 
b/python/tvm/relax/transform/__init__.py
index 35be359e76..4d841a8e7b 100644
--- a/python/tvm/relax/transform/__init__.py
+++ b/python/tvm/relax/transform/__init__.py
@@ -51,6 +51,7 @@ from .transform import (
     MetaScheduleTuneIRMod,
     MetaScheduleTuneTIR,
     Normalize,
+    NormalizeGlobalVar,
     PatternCheckContext,
     RealizeVDevice,
     RemovePurityChecking,
diff --git a/python/tvm/relax/transform/transform.py 
b/python/tvm/relax/transform/transform.py
index 5bdea37b50..049bce7428 100644
--- a/python/tvm/relax/transform/transform.py
+++ b/python/tvm/relax/transform/transform.py
@@ -284,6 +284,21 @@ def Normalize() -> tvm.ir.transform.Pass:
     return _ffi_api.Normalize()  # type: ignore
 
 
+def NormalizeGlobalVar() -> tvm.ir.transform.Pass:
+    """Possibly rename the GlobalVar in an IRModule to ensure these properties:
+
+    1. (Invariant) First ensure every public function has the same name as its 
"global_symbol"
+    attribute
+    2. To ensure 1., we may need to rename private functions with conflicting 
names;
+    3. Finally, the name of every GlobalVar is unique in the IRModule.
+
+    Returns
+    -------
+    ret: tvm.ir.transform.Pass
+    """
+    return _ffi_api.NormalizeGlobalVar()  # type: ignore
+
+
 def CanonicalizeBindings() -> tvm.ir.transform.Pass:
     """
     Canonicalizes variable definitions
diff --git a/src/relax/analysis/well_formed.cc 
b/src/relax/analysis/well_formed.cc
index 9eed78d270..12fa30b21b 100644
--- a/src/relax/analysis/well_formed.cc
+++ b/src/relax/analysis/well_formed.cc
@@ -26,7 +26,7 @@
  * This pass will check:
  *    1. Each Expr should have `struct_info_` field already populated, when
  *      `check_struct_info` is true.
- *    2. GlobalVars are defined before use.
+ *    2. GlobalVars are defined before use. And all GlobalVars have different 
names.
  *    3. When a Function has a corresponding GlobalVar and a `global_symbol`
  *       attribute, the name of the GlobalVar must equal the value of the
  *       `global_symbol` attribute value.
@@ -126,6 +126,9 @@ class WellFormedChecker : public relax::ExprVisitor,
   }
 
   void CheckGlobalVarAndGsymbolConsistency(GlobalVar var, Function func) {
+    // the uniqueness of all global vars are ensured by 
IRModule->global_var_map_, so do not need
+    // to check again
+
     // check name in global var and gsymbol
     Optional<String> gsymbol = func->GetAttr<String>(tvm::attr::kGlobalSymbol);
     if (gsymbol.defined() && gsymbol != var->name_hint) {
diff --git a/src/relax/ir/block_builder.cc b/src/relax/ir/block_builder.cc
index 210af591b5..fda31e44a9 100644
--- a/src/relax/ir/block_builder.cc
+++ b/src/relax/ir/block_builder.cc
@@ -27,6 +27,7 @@
 #include <tvm/relax/op_attr_types.h>
 #include <tvm/relax/struct_info.h>
 #include <tvm/relax/struct_info_functor.h>
+#include <tvm/relax/transform.h>
 #include <tvm/relax/type.h>
 #include <tvm/relay/op.h>
 #include <tvm/runtime/registry.h>
@@ -72,6 +73,8 @@ class BlockBuilderImpl : public BlockBuilderNode {
 
   IRModule GetContextIRModule() const final { return context_mod_; }
 
+  IRModule Finalize() final { return 
transform::NormalizeGlobalVar()(context_mod_); }
+
   GlobalVar AddFunction(const BaseFunc& func, String func_name_hint) final {
     LazyInitCtxFuncDedupMap();
     auto it = ctx_func_dedup_map_->find(func);
@@ -1002,6 +1005,9 @@ TVM_REGISTER_GLOBAL("relax.BlockBuilderUpdateFunction")
 TVM_REGISTER_GLOBAL("relax.BlockBuilderGetContextIRModule")
     .set_body_method<BlockBuilder>(&BlockBuilderNode::GetContextIRModule);
 
+TVM_REGISTER_GLOBAL("relax.BlockBuilderFinalize")
+    .set_body_method<BlockBuilder>(&BlockBuilderNode::Finalize);
+
 TVM_REGISTER_GLOBAL("relax.BlockBuilderCurrentBlockIsDataFlow")
     .set_body_method<BlockBuilder>(&BlockBuilderNode::CurrentBlockIsDataFlow);
 
diff --git a/src/relax/transform/normalize.cc b/src/relax/transform/normalize.cc
index fdd2ccc17e..6b45a4c8e9 100644
--- a/src/relax/transform/normalize.cc
+++ b/src/relax/transform/normalize.cc
@@ -170,6 +170,99 @@ class NormalizeMutator : public ExprMutatorBase {
 
 Expr Normalize(const Expr& e) { return NormalizeMutator().VisitExpr(e); }
 
+class GlobalVarNormalizer : private ExprMutator {
+ public:
+  static IRModule Normalize(const IRModule& m) {
+    GlobalVarNormalizer renamer(m);
+    return renamer.RenameModule();
+  }
+
+ private:
+  explicit GlobalVarNormalizer(const IRModule& m) : ExprMutator(), module_(m), 
name_supply_("") {}
+
+  using ExprMutator::VisitExpr_;
+
+  IRModule RenameModule() {
+    // Step 1. Add public functions (functions with global_symbol attributes)
+    auto name_changes = AddPublicFunctions();
+    if (!name_changes) {
+      return module_;
+    }
+
+    // Step 2. Rename private functions
+    AddPrivateFunctions();
+
+    // Step 3. Substitute global vars in functions
+    for (auto [gvar, func] : module_->functions) {
+      if (!func->IsInstance<FunctionNode>()) {
+        continue;
+      }
+      auto new_func = Downcast<BaseFunc>(this->VisitExpr(func));
+      builder_->UpdateFunction(gvar_map_[gvar], new_func);
+    }
+
+    // Step 4. Update the original module (because we do not want to copy all 
metadata to the new
+    // module)
+    auto after_module = builder_->GetContextIRModule();
+    auto module_node = module_.CopyOnWrite();
+    module_node->functions = after_module->functions;
+    module_node->global_var_map_ = after_module->global_var_map_;
+    return module_;
+  }
+
+  /**
+   * \brief Add public functions to the builder, and update the name supplier.
+   * \return true if any name changes are made.
+   */
+  bool AddPublicFunctions() {
+    bool name_changes = false;
+    for (const auto& [gvar, func] : module_->functions) {
+      auto global_symbol = func->GetAttr<String>("global_symbol");
+      if (!global_symbol) {
+        continue;
+      }
+
+      auto global_symbol_value = global_symbol.value();
+      CHECK(!name_supply_->ContainsName(global_symbol_value))
+          << "IRModule contains duplicate global symbol: " << 
global_symbol_value;
+      name_supply_->ReserveName(global_symbol_value);
+      auto new_gvar = builder_->AddFunction(func, global_symbol_value);
+      gvar_map_.Set(gvar, new_gvar);
+
+      if (global_symbol.value() != gvar->name_hint) {
+        name_changes = true;
+      }
+    }
+    return name_changes;
+  }
+
+  /**
+   * \brief Add private functions to the builder with names provided by name 
supplier. Renaming may
+   * happen if the name of any function conflicts with the name of a public 
function.
+   */
+  void AddPrivateFunctions() {
+    for (auto [gvar, func] : module_->functions) {
+      auto global_symbol = func->GetAttr<String>("global_symbol");
+      if (global_symbol) {
+        continue;
+      }
+
+      auto new_name = name_supply_->FreshName(gvar->name_hint, false, false);
+      auto new_gvar = builder_->AddFunction(func, new_name);
+      gvar_map_.Set(gvar, new_gvar);
+    }
+  }
+
+  Expr VisitExpr_(const GlobalVarNode* op) final {
+    ICHECK(gvar_map_.count(GetRef<GlobalVar>(op)));
+    return gvar_map_[GetRef<GlobalVar>(op)];
+  }
+
+  IRModule module_;
+  NameSupply name_supply_;
+  Map<GlobalVar, GlobalVar> gvar_map_;
+};
+
 namespace transform {
 
 Pass Normalize() {
@@ -180,6 +273,16 @@ Pass Normalize() {
 
 TVM_REGISTER_GLOBAL("relax.transform.Normalize").set_body_typed(Normalize);
 
+Pass NormalizeGlobalVar() {
+  runtime::TypedPackedFunc<IRModule(IRModule, PassContext)> pass_func =
+      [=](IRModule mod, PassContext pc) { return 
GlobalVarNormalizer::Normalize(mod); };
+  return CreateModulePass(/*pass_function=*/pass_func,
+                          /*opt_level=*/0,
+                          /*pass_name=*/"NormalizeGlobalVar",
+                          /*required=*/{});
+}
+TVM_REGISTER_GLOBAL("relax.transform.NormalizeGlobalVar").set_body_typed(NormalizeGlobalVar);
+
 }  // namespace transform
 
 }  // namespace relax
diff --git a/tests/python/relax/test_blockbuilder_core.py 
b/tests/python/relax/test_blockbuilder_core.py
index f09b7bc6f5..e1cbe37b18 100644
--- a/tests/python/relax/test_blockbuilder_core.py
+++ b/tests/python/relax/test_blockbuilder_core.py
@@ -28,9 +28,11 @@ from tvm.script import relax as R, tir as T
 from tvm.tir.function import PrimFunc
 
 
[email protected]_func("test.blockbuilder.nop")
-def nop():
-    pass
[email protected](scope="module")
+def register_nop():
+    @tvm.register_func("test.blockbuilder.nop")
+    def nop():
+        pass
 
 
 def test_block_builder():
@@ -91,7 +93,7 @@ def test_function_single_block():
         assert gv0.name_hint == "gv"
         bb.emit_func_output(gv0)
 
-    func = bb.get()["func"]
+    func = bb.finalize()["func"]
     assert func.params[0] == x
     assert func.params[1] == y
     assert func.body.body == gv0
@@ -121,7 +123,7 @@ def test_function_multi_blocks():
             gv2 = bb.emit_output(gv1)
         bb.emit_func_output(gv2)
 
-    func = bb.get()["func"]
+    func = bb.finalize()["func"]
 
     assert_structural_equal(gv2.struct_info, rx.TensorStructInfo([m, n], 
"float16"))
     assert func.params[0] == x
@@ -134,35 +136,41 @@ def test_function_multi_blocks():
 
 
 def test_multi_functions():
-    m = tir.Var("m", "int64")
-    n = tir.Var("n", "int64")
-    x = rx.Var("x", rx.TensorStructInfo([m, n], "float16"))
-    y = rx.Var("y", rx.TensorStructInfo([n], "float16"))
     bb = rx.BlockBuilder()
 
-    with bb.function("func1", [x, y]):
+    m_1 = tir.Var("m", "int64")
+    n_1 = tir.Var("n", "int64")
+    x_1 = rx.Var("x", rx.TensorStructInfo([m_1, n_1], "float16"))
+    y_1 = rx.Var("y", rx.TensorStructInfo([n_1], "float16"))
+
+    with bb.function("func1", [x_1, y_1]):
         with bb.dataflow():
-            lv0 = bb.emit(rx.op.add(x, y))
+            lv0 = bb.emit(rx.op.add(x_1, y_1))
             assert lv0.name_hint == "lv"
             gv0 = bb.emit_output(lv0)
         bb.emit_func_output(gv0)
 
-    with bb.function("func2", [x, y]):
+    m_2 = tir.Var("m", "int64")
+    n_2 = tir.Var("n", "int64")
+    x_2 = rx.Var("x", rx.TensorStructInfo([m_2, n_2], "float16"))
+    y_2 = rx.Var("y", rx.TensorStructInfo([n_2], "float16"))
+
+    with bb.function("func2", [x_2, y_2]):
         with bb.dataflow():
-            lv0 = bb.emit(rx.op.add(y, x))
+            lv0 = bb.emit(rx.op.add(y_2, x_2))
             # TODO(@yuchen): enable block builder to reset local var unique 
name map
             assert lv0.name_hint == "lv1"
             gv0 = bb.emit_output(lv0)
         bb.emit_func_output(gv0)
 
-    mod = bb.get()
+    mod = bb.finalize()
     func1 = mod["func1"]
-    assert func1.params[0] == x
-    assert func1.params[1] == y
+    assert func1.params[0] == x_1
+    assert func1.params[1] == y_1
     assert len(func1.body.blocks) == 1
     func2 = mod["func2"]
-    assert func2.params[0] == x
-    assert func2.params[1] == y
+    assert func2.params[0] == x_2
+    assert func2.params[1] == y_2
     assert len(func2.body.blocks) == 1
 
 
@@ -223,7 +231,7 @@ def test_emit_match_cast():
             gv0 = bb.emit_output(lv1)
 
         bb.emit_func_output(gv0)
-    func = bb.get()["func"]
+    func = bb.finalize()["func"]
     block = func.body.blocks[0]
     b0, b1 = block.bindings[:2]
     assert isinstance(b0, rx.MatchCast)
@@ -252,7 +260,7 @@ def test_emit_match_cast_binding_in_dataflow_block():
             bb.emit_output(gv)
         bb.emit_func_output(x)
 
-    func = bb.get()["main"]
+    func = bb.finalize()["main"]
     block = func.body.blocks[0]
     b0 = block.bindings[0]
     assert isinstance(b0, rx.MatchCast)
@@ -353,7 +361,7 @@ def test_call_te():
             out = bb.emit_output(bb.call_te(te_func, [x, y], {"C": z}, 
msg="hello"))
         bb.emit_func_output(out)
 
-    mod = bb.get()
+    mod = bb.finalize()
     rx_func = mod["rx_func"]
 
     assert rx_func.params[0] == x
@@ -372,7 +380,7 @@ def test_call_te_unique_tensor_name():
         gv = bb.emit_te(topi.nn.matmul, x, y)
         bb.emit_func_output(gv)
 
-    f_matmul = bb.get()["matmul"]
+    f_matmul = bb.finalize()["matmul"]
     param_A = f_matmul.params[0]
     param_B = f_matmul.params[1]
     buffer_A = f_matmul.buffer_map[param_A]
@@ -411,7 +419,7 @@ def test_emit_te():
         out = bb.emit_te(te_func, [x, y], {"C": z}, msg="hello")
         bb.emit_func_output(out)
 
-    mod = bb.get()
+    mod = bb.finalize()
     rx_func = mod["rx_func"]
 
     def get_tir_func():
@@ -453,13 +461,13 @@ def test_emit_te_multiple():
         B = te.compute((128, 128), lambda i, j: A[i, j] + 1)
         return B
 
-    with bb.function("rx_func", [x, y]):
+    with bb.function("rx_func", [x, y, z]):
         x1 = bb.emit_te(te_func, x)
         y1 = bb.emit_te(te_func, y)
         z1 = bb.emit_te(te_func, z)
         bb.emit_func_output(z1)
 
-    mod = bb.get()
+    mod = bb.finalize()
     rx_func = mod["rx_func"]
 
     prim_func = []
@@ -488,7 +496,7 @@ def test_emit_te_multiple_output():
         z = rx.TupleGetItem(y, 0)
         bb.emit_func_output([y, z])
 
-    rx_func = bb.get()["rx_func"]
+    rx_func = bb.finalize()["rx_func"]
 
     # check call tir output shape is a Tuple of ShapeExpr
     assert rx_func.params[0] == x
@@ -511,7 +519,7 @@ def test_emit_te_extern():
         out = bb.emit_te(tvm.contrib.cblas.matmul, x, y, transa=False, 
transb=False)
         bb.emit_func_output(out)
 
-    mod = bb.get()
+    mod = bb.finalize()
     rx_func = mod["rx_cblas_matmul"]
 
     # check Relax function calls TIR function with call_tir call
@@ -540,7 +548,7 @@ def test_emit_te_prim_value():
         out = bb.emit_te(topi.clip, x, a_min, a_max)
         bb.emit_func_output(out)
 
-    rx_func = bb.get()["rx_clip"]
+    rx_func = bb.finalize()["rx_clip"]
 
     # check Relax function calls TIR function with call_tir call
     assert rx_func.params[0] == x
@@ -649,7 +657,7 @@ def test_emit_nested_tuple(emit_nested_tuple):
             output = (scalars, x, y)
             bb.emit_func_output(output)
 
-        return bb.get()["func"]
+        return bb.finalize()["func"]
 
     def make_expected(emit_nested_tuple: bool):
         if emit_nested_tuple:
@@ -683,5 +691,42 @@ def test_emit_nested_tuple(emit_nested_tuple):
     tvm.ir.assert_structural_equal(expected, actual)
 
 
[email protected]_well_formed_check_before_transform
+def test_finalize_public_private_name_conflict():
+    # tir call
+    bb = rx.BlockBuilder()
+
+    def te_zero():
+        return topi.full((), "int64", tir.IntImm("int64", 0))
+
+    def te_one():
+        return topi.full((), "int64", tir.IntImm("int64", 1))
+
+    with bb.function("func", []):
+        gv0 = bb.emit_te(te_zero, primfunc_name_hint="func")
+        gv1 = bb.emit_te(te_one, primfunc_name_hint="func")
+        bb.emit_func_output((gv0, gv1))
+
+    mod = bb.finalize()
+    assert rx.analysis.well_formed(mod)
+
+    # relax function call
+    bb = rx.BlockBuilder()
+
+    with bb.function("func", [], private=True):
+        gvar = bb.emit_func_output(rx.const(0, "int64"))
+
+    with bb.function("func", [], private=True):
+        gv0 = bb.emit(rx.Call(gvar, []))
+        gvar1 = bb.emit_func_output(gv0)
+
+    with bb.function("func", []):
+        gv0 = bb.emit(rx.Call(gvar1, []))
+        bb.emit_func_output(gv0)
+
+    mod = bb.finalize()
+    assert rx.analysis.well_formed(mod)
+
+
 if __name__ == "__main__":
     tvm.testing.main()
diff --git a/tests/python/relax/test_transform_normalize_global_var.py 
b/tests/python/relax/test_transform_normalize_global_var.py
new file mode 100644
index 0000000000..0a26ffc8e6
--- /dev/null
+++ b/tests/python/relax/test_transform_normalize_global_var.py
@@ -0,0 +1,98 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import pytest
+
+import tvm
+import tvm.testing
+from tvm import relax
+from tvm import tir
+from tvm.ir.base import assert_structural_equal
+
+import tvm.script
+from tvm.script import tir as T, relax as R, ir as I
+
+
[email protected]_well_formed_check_before_transform
+def test_normalize_relax_function():
+    @I.ir_module
+    class Before:
+        @R.function(private=True)
+        def f():
+            return R.const(1, "int32")
+
+        @R.function
+        def f1():
+            R.func_attr({"global_symbol": "f"})
+            cls = Before
+            gv: R.Tensor((), dtype="int32") = cls.f()
+            return gv
+
+    @I.ir_module
+    class Expected:
+        @R.function
+        def f():
+            cls = Expected
+            gv: R.Tensor((), dtype="int32") = cls.f1()
+            return gv
+
+        @R.function(private=True)
+        def f1():
+            return R.const(1, "int32")
+
+    After = relax.transform.NormalizeGlobalVar()(Before)
+
+    assert not relax.analysis.well_formed(Before)
+    assert relax.analysis.well_formed(After)
+    assert_structural_equal(After, Expected)
+
+
[email protected]_well_formed_check_before_transform
+def test_normalize_tir_function():
+    @I.ir_module
+    class Before:
+        @T.prim_func(private=True)
+        def f(x: T.Buffer((1,), "int32")):
+            x[0] = T.int32(0)
+
+        @R.function
+        def f1():
+            R.func_attr({"global_symbol": "f"})
+            cls = Before
+            gv: R.Tensor((), dtype="int32") = R.call_tir(cls.f, (), 
R.Tensor((1,), dtype="int32"))
+            return gv
+
+    @I.ir_module
+    class Expected:
+        @T.prim_func(private=True)
+        def f1(x: T.Buffer((1,), "int32")):
+            x[0] = 0
+
+        @R.function
+        def f() -> R.Tensor((1,), dtype="int32"):
+            cls = Expected
+            gv = R.call_tir(cls.f1, R.tuple(), out_sinfo=R.Tensor((1,), 
dtype="int32"))
+            return gv
+
+    After = relax.transform.NormalizeGlobalVar()(Before)
+
+    assert not relax.analysis.well_formed(Before)
+    assert relax.analysis.well_formed(After)
+    assert_structural_equal(After, Expected)
+
+
+if __name__ == "__main__":
+    tvm.testing.main()

Reply via email to