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 b2c61162f0 [Relax][Bugfix] Bind symbolic variables in R.match_cast 
(#17034)
b2c61162f0 is described below

commit b2c61162f006504b192493e9ceeac9b89a87da65
Author: Eric Lunderberg <[email protected]>
AuthorDate: Tue May 28 18:52:01 2024 -0500

    [Relax][Bugfix] Bind symbolic variables in R.match_cast (#17034)
    
    Prior to this commit, variable replacement by `BindSymbolicVars` would
    fail to replace variables that occur within a `relax::MatchCast` node.
    This pattern is rare, because the `bind_symbolic_vars` method can only
    replace variables that are exposed as part of the function signature,
    and most uses of `relax::MatchCast` act as a definition for symbolic
    variables that are not exposed through the function signature.  This
    pattern is well-formed, though, since the `relax::MatchCast` node can
    also act as a user of previously-defined symbolic variables.
    
    The root cause for this bug was in the `ExprMutator` visitor for
    `relax::MatchCast`, which did not visit the struct info field.  As a
    result, the virtual `ExprMutator::VisitPrimExpr` function was not
    called for expressions that occur within the `StructInfo` of a
    `relax::MatchCast`.  This commit updates `ExprMutator` to resolve this
    bug, and applies an analogous fix for `ExprVisitor`.
    
    Co-authored-by: Chris Sullivan <[email protected]>
---
 src/relax/ir/expr_functor.cc                  | 22 ++++++++++++++++------
 tests/python/relax/test_bind_symbolic_vars.py | 22 ++++++++++++++++++++++
 2 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/src/relax/ir/expr_functor.cc b/src/relax/ir/expr_functor.cc
index dbfaf60fec..63c74db7e3 100644
--- a/src/relax/ir/expr_functor.cc
+++ b/src/relax/ir/expr_functor.cc
@@ -257,6 +257,7 @@ RELAX_EXPR_VISITOR_VISIT_BINDING_IMPL(DataTypeImmNode);
 
 void ExprVisitor::VisitBinding_(const MatchCastNode* binding) {
   this->VisitExpr(binding->value);
+  this->VisitExprDepStructInfoField(binding->struct_info);
   this->VisitVarDef(binding->var);
 }
 
@@ -690,16 +691,25 @@ void ExprMutator::ReEmitBinding(const VarBindingNode* 
binding, Expr new_value) {
 }
 
 void ExprMutator::VisitBinding_(const MatchCastNode* binding) {
-  Var new_var = this->VisitVarDef(binding->var);
   Expr new_value = this->VisitExpr(binding->value);
+  StructInfo new_struct_info = 
this->VisitExprDepStructInfoField(binding->struct_info);
 
-  // re-emit old binding if nothing changes
-  if (new_var.same_as(binding->var) && new_value.same_as(binding->value)) {
+  Var new_var = this->VisitVarDef(binding->var);
+
+  if (new_var.same_as(binding->var) && new_value.same_as(binding->value) &&
+      new_struct_info.same_as(binding->struct_info)) {
+    // re-emit old binding if nothing changes
     builder_->EmitNormalized(GetRef<MatchCast>(binding));
-  } else {
-    new_value = builder_->NormalizeArgument(new_value);
-    builder_->EmitNormalized(MatchCast(new_var, new_value, 
binding->struct_info, binding->span));
+    return;
   }
+
+  new_value = builder_->NormalizeArgument(new_value);
+  new_var = WithStructInfo(new_var, new_struct_info);
+
+  var_remap_[binding->var->vid] = new_var;
+  var_remap_[new_var->vid] = new_var;
+
+  builder_->EmitNormalized(MatchCast(new_var, new_value, new_struct_info, 
binding->span));
 }
 
 BindingBlock ExprMutator::VisitBindingBlock_(const BindingBlockNode* block) {
diff --git a/tests/python/relax/test_bind_symbolic_vars.py 
b/tests/python/relax/test_bind_symbolic_vars.py
index 82798c56df..18246d224b 100644
--- a/tests/python/relax/test_bind_symbolic_vars.py
+++ b/tests/python/relax/test_bind_symbolic_vars.py
@@ -286,5 +286,27 @@ def test_bind_strided_slice():
     tvm.ir.assert_structural_equal(expected, after)
 
 
+def test_bind_inside_match_cast():
+    """Symbolic variables may occur within R.match_cast"""
+
+    @R.function(private=True)
+    def before(A: R.Tensor(["M", "N"]), B: R.Tensor(ndim=2)):
+        M = T.int64()
+        N = T.int64()
+        C = R.match_cast(B, R.Tensor([M, N]))
+        D = R.add(A, C)
+        return D
+
+    @R.function(private=True)
+    def expected(A: R.Tensor(["M", 32]), B: R.Tensor(ndim=2)):
+        M = T.int64()
+        C = R.match_cast(B, R.Tensor([M, 32]))
+        D = R.add(A, C)
+        return D
+
+    after = before.bind_symbolic_vars({"N": 32})
+    tvm.ir.assert_structural_equal(expected, after)
+
+
 if __name__ == "__main__":
     tvm.testing.main()

Reply via email to