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

masahi 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 62a5e7acf5 [Relay] Improve the "clip" op optimization in simplify expr 
pass (#15068)
62a5e7acf5 is described below

commit 62a5e7acf537554e078a6d5924616be3d273a1dd
Author: 电线杆 <[email protected]>
AuthorDate: Thu Jun 15 12:02:50 2023 +0800

    [Relay] Improve the "clip" op optimization in simplify expr pass (#15068)
    
    * improve the simplification of clip op in simplify_expr
    
    * 1. the arg of clip is forced to not be qnn.requantize and ann.add. This 
is to avoid destroying the structure required by LegalizeQnnOpForDnnl; 2. code 
reformatted.
    
    * Fix the treatment of bfloat16 in function CheckDataTypeMaxMinValue. Add a 
test case of bfloat16 in test_pass_simplify_expr.
    
    * code reformatted
    
    * fix a typo
---
 src/relay/transforms/simplify_expr.cc         | 54 ++++++++++--------
 tests/python/relay/test_pass_simplify_expr.py | 82 +++++++++++++++++++++------
 2 files changed, 96 insertions(+), 40 deletions(-)

diff --git a/src/relay/transforms/simplify_expr.cc 
b/src/relay/transforms/simplify_expr.cc
index a557f2496b..fa3348b95a 100644
--- a/src/relay/transforms/simplify_expr.cc
+++ b/src/relay/transforms/simplify_expr.cc
@@ -161,17 +161,15 @@ class SimplifyConsecutiveCast : public DFPatternRewrite {
 };
 
 bool CheckDataTypeMaxMinValue(DataType dtype, double min_value, double 
max_value) {
+  double lbound{}, ubound{};
   if (dtype.is_int() || dtype.is_uint()) {
-    double ubound = 
static_cast<double>(Downcast<IntImm>(tvm::max_value(dtype))->value);
-    double lbound = 
static_cast<double>(Downcast<IntImm>(tvm::min_value(dtype))->value);
-    return ubound == max_value && lbound == min_value;
-  } else if (dtype.is_float()) {
-    double ubound = Downcast<FloatImm>(tvm::max_value(dtype))->value;
-    double lbound = Downcast<FloatImm>(tvm::min_value(dtype))->value;
-    return ubound == max_value && lbound == min_value;
+    ubound = 
static_cast<double>(Downcast<IntImm>(tvm::max_value(dtype))->value);
+    lbound = 
static_cast<double>(Downcast<IntImm>(tvm::min_value(dtype))->value);
+  } else if (dtype.is_float() || dtype.is_bfloat16()) {
+    ubound = Downcast<FloatImm>(tvm::max_value(dtype))->value;
+    lbound = Downcast<FloatImm>(tvm::min_value(dtype))->value;
   }
-
-  return false;
+  return max_value >= ubound && min_value <= lbound;
 }
 
 /*!
@@ -224,8 +222,8 @@ class SimplifyClipAndConsecutiveCast : public 
DFPatternRewrite {
 };
 
 /*!
- * \brief SimplifyCastClip matches the pattern cast->clip and remove redundant 
Cast based on Clip
- *    min/max values and min/max values of Cast target data type.
+ * \brief SimplifyClip removes redundant Clip based on its a_min/a_max values 
and the min/max values
+ * of the data type.
  *
  * Example:
  *   %1 = cast(%0, dtype="uint8") [type=uint8]
@@ -234,30 +232,38 @@ class SimplifyClipAndConsecutiveCast : public 
DFPatternRewrite {
  * Optimized to (remove Clip):
  *   %1 = cast(%0, dtype="uint8") [type=uint8]
  */
-class SimplifyCastClip : public DFPatternRewrite {
+class SimplifyClip : public DFPatternRewrite {
  public:
-  SimplifyCastClip() {
-    cast_ = IsOp("cast")({IsWildcard()});
-    pattern_ = IsOp("clip")({cast_});
+  SimplifyClip() {
+    x_ = IsWildcard();
+    pattern_ = IsOp("clip")({x_});
   }
 
   Expr Callback(const Expr& pre, const Expr& post,
                 const Map<DFPattern, Array<Expr>>& node_map) const override {
-    auto cast = Downcast<Call>(node_map[cast_][0]);
-    DataType cast_dtype = Downcast<TensorType>(cast->checked_type())->dtype;
+    DataType cast_dtype = Downcast<TensorType>(pre->checked_type())->dtype;
 
-    auto clip = Downcast<Call>(post);
-    const CallNode* clip_node = clip.as<CallNode>();
+    const CallNode* clip_node = post.as<CallNode>();
     const ClipAttrs* clip_attrs = clip_node->attrs.as<ClipAttrs>();
 
+    // TODO(kfeng123): For now, the arg of "clip" is forced to not be 
"qnn.requantize" and
+    // "qnn.add". This is to avoid destroying the structure required by 
LegalizeQnnOpForDnnl
+    auto child{post.as<CallNode>()->args[0].as<CallNode>()};
+    if (child && child->op.as<OpNode>()) {
+      String op_name{child->op.as<OpNode>()->name};
+      if (op_name == "qnn.requantize" || op_name == "qnn.add") {
+        return post;
+      }
+    }
+
     if (CheckDataTypeMaxMinValue(cast_dtype, clip_attrs->a_min, 
clip_attrs->a_max)) {
-      return node_map[cast_][0];
+      return node_map[x_][0];
     }
     return post;
   }
 
  protected:
-  DFPattern clip_, cast_;
+  DFPattern x_;
 };
 
 /*!
@@ -992,7 +998,7 @@ class SimplifyBinomial : public DFPatternRewrite {
   DFPattern y_;
 };
 
-/*! \brief Simplifying x/sqrt to x*sqrt */
+/*! \brief Simplifying x/sqrt to x*rsqrt */
 class SimplifyRSqrt : public DFPatternRewrite {
  public:
   SimplifyRSqrt() {
@@ -1085,7 +1091,7 @@ Expr SimplifyExpr(const Expr& expr, const IRModule& mod) {
   composer.AddRewrite<SimplifyDQArgMin>();
   composer.AddRewrite<SimplifyDQArgSort>();
   composer.AddRewrite<SimplifyClipAndConsecutiveCast>();
-  composer.AddRewrite<SimplifyCastClip>();
+  composer.AddRewrite<SimplifyClip>();
   composer.AddRewrite<SimplifyBinomial>();
   return RewritePatterns(composer.MakeCallbacks(), expr, mod);
 }
@@ -1099,7 +1105,7 @@ Expr SimplifyExprPostAlterOp(const Expr& expr, const 
IRModule& mod) {
   composer.AddRewrite<SimplifySameCast>();
   composer.AddRewrite<SimplifyConsecutiveCast>();
   composer.AddRewrite<SimplifyClipAndConsecutiveCast>();
-  composer.AddRewrite<SimplifyCastClip>();
+  composer.AddRewrite<SimplifyClip>();
   return RewritePatterns(composer.MakeCallbacks(), expr, mod);
 }
 
diff --git a/tests/python/relay/test_pass_simplify_expr.py 
b/tests/python/relay/test_pass_simplify_expr.py
index 4edb85f2d7..b117c91d1c 100644
--- a/tests/python/relay/test_pass_simplify_expr.py
+++ b/tests/python/relay/test_pass_simplify_expr.py
@@ -722,34 +722,84 @@ def test_simplify_dq_argsort():
 
 
 def test_simplify_clip_cast():
-    x = relay.var("x", shape=(4, 8), dtype="int32")
+    def before1():
+        x = relay.var("x", shape=(4, 8), dtype="int32")
+        clip = relay.clip(x, a_min=0.0, a_max=255.0)
+        cast = relay.cast(clip, "uint8")
+        cast = relay.cast(cast, "int32")
+        return relay.Function([x], cast)
 
-    def before():
+    def expected1():
+        x = relay.var("x", shape=(4, 8), dtype="int32")
+        clip = relay.clip(x, a_min=0.0, a_max=255.0)
+        return relay.Function([x], clip)
+
+    def before2():
+        x = relay.var("x", shape=(4, 8), dtype="int32")
         clip = relay.clip(x, a_min=0.0, a_max=255.0)
         cast = relay.cast(clip, "uint8")
-        return relay.cast(cast, "int32")
+        cast = relay.cast(cast, "int32")
+        return relay.Function([x], cast)
 
-    def expected():
-        return relay.clip(x, a_min=0.0, a_max=255.0)
+    def expected2():
+        x = relay.var("x", shape=(4, 8), dtype="int32")
+        clip = relay.clip(x, a_min=0.0, a_max=255.0)
+        return relay.Function([x], clip)
 
-    opt = run_opt_pass(before(), transform.SimplifyExpr())
-    ref = run_infer_type(expected())
-    assert tvm.ir.structural_equal(opt, ref)
+    for before, expected in [
+        [before1(), expected1()],
+        [before2(), expected2()],
+    ]:
+        after = run_opt_pass(before, transform.SimplifyExpr())
+        expected = run_opt_pass(expected, transform.InferType())
+        assert tvm.ir.structural_equal(after, expected), "\nafter: {} 
\nexpected: {}".format(
+            after, expected
+        )
 
 
 def test_simplify_cast_clip():
-    x = relay.var("x", shape=(4, 8), dtype="int32")
+    def before1():
+        x = relay.var("x", shape=(4, 8), dtype="int32")
+        cast = relay.cast(x, "uint8")
+        clip = relay.clip(cast, a_min=0.0, a_max=255.0)
+        return relay.Function([x], clip)
 
-    def before():
+    def expected1():
+        x = relay.var("x", shape=(4, 8), dtype="int32")
         cast = relay.cast(x, "uint8")
-        return relay.clip(cast, a_min=0.0, a_max=255.0)
+        return relay.Function([x], cast)
 
-    def expected():
-        return relay.cast(x, "uint8")
+    def before2():
+        x = relay.var("x", shape=(4, 8), dtype="uint8")
+        clip = relay.clip(x, a_min=0.0, a_max=255.0)
+        return relay.Function([x], clip)
 
-    opt = run_opt_pass(before(), transform.SimplifyExpr())
-    ref = run_infer_type(expected())
-    assert tvm.ir.structural_equal(opt, ref)
+    def expected2():
+        x = relay.var("x", shape=(4, 8), dtype="uint8")
+        return relay.Function([x], x)
+
+    def before3():
+        x = relay.var("x", shape=(4, 8), dtype="float32")
+        cast = relay.cast(x, "bfloat16")
+        clip = relay.clip(cast, a_min=-0.2, a_max=0.4)
+        return relay.Function([x], clip)
+
+    def expected3():
+        x = relay.var("x", shape=(4, 8), dtype="float32")
+        cast = relay.cast(x, "bfloat16")
+        clip = relay.clip(cast, a_min=-0.2, a_max=0.4)
+        return relay.Function([x], clip)
+
+    for before, expected in [
+        [before1(), expected1()],
+        [before2(), expected2()],
+        [before3(), expected3()],
+    ]:
+        after = run_opt_pass(before, transform.SimplifyExpr())
+        expected = run_opt_pass(expected, transform.InferType())
+        assert tvm.ir.structural_equal(after, expected), "\nafter: {} 
\nexpected: {}".format(
+            after, expected
+        )
 
 
 def test_simplify_add():

Reply via email to