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

hongyij 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 28c85f0dc2 [METAL] Fix vectorized select (#14846)
28c85f0dc2 is described below

commit 28c85f0dc24e41d14ebd5f03ad2a021a99290096
Author: Tianqi Chen <[email protected]>
AuthorDate: Sun May 14 17:20:19 2023 -0400

    [METAL] Fix vectorized select (#14846)
    
    This PR fixes the codegen for vectorized select in metal.
    Also enhances arithmetics to cover better constant bound.
---
 src/arith/const_int_bound.cc                       |  2 +
 src/target/source/codegen_metal.cc                 |  5 +++
 src/target/source/codegen_metal.h                  |  1 +
 src/tir/transforms/flatten_buffer.cc               |  2 +
 src/tir/transforms/lower_intrin.cc                 | 10 +++--
 .../python/unittest/test_arith_const_int_bound.py  | 18 +++++++++
 tests/python/unittest/test_target_codegen_metal.py | 46 ++++++++++++++++------
 7 files changed, 69 insertions(+), 15 deletions(-)

diff --git a/src/arith/const_int_bound.cc b/src/arith/const_int_bound.cc
index 4c048bfa73..68ade3bb54 100644
--- a/src/arith/const_int_bound.cc
+++ b/src/arith/const_int_bound.cc
@@ -177,6 +177,8 @@ class ConstIntBoundAnalyzer::Impl
     return Union(a, b);
   }
 
+  Entry VisitExpr_(const BroadcastNode* op) final { return 
VisitExpr(op->value); }
+
   Entry VisitExpr_(const CastNode* op) final {
     Entry a;
 
diff --git a/src/target/source/codegen_metal.cc 
b/src/target/source/codegen_metal.cc
index 44da240dd5..9288c94e3d 100644
--- a/src/target/source/codegen_metal.cc
+++ b/src/target/source/codegen_metal.cc
@@ -289,6 +289,11 @@ void CodeGenMetal::PrintStorageScope(const std::string& 
scope, std::ostream& os)
   }
 }
 
+void CodeGenMetal::VisitExpr_(const SelectNode* op, std::ostream& os) {  // 
NOLINT(*)
+  os << "select(" << PrintExpr(op->false_value) << ", " << 
PrintExpr(op->true_value) << ", "
+     << PrintExpr(op->condition) << ")";
+}
+
 void CodeGenMetal::VisitExpr_(const BroadcastNode* op, std::ostream& os) {  // 
NOLINT(*)
   std::string v = PrintExpr(op->value);
   PrintType(op->dtype, os);
diff --git a/src/target/source/codegen_metal.h 
b/src/target/source/codegen_metal.h
index 2564389609..36be10d163 100644
--- a/src/target/source/codegen_metal.h
+++ b/src/target/source/codegen_metal.h
@@ -50,6 +50,7 @@ class CodeGenMetal final : public CodeGenC {
   // print store of single element.
   void PrintVecElemStore(const std::string& vec, DataType t, int i, const 
std::string& value) final;
   // overload visitor
+  void VisitExpr_(const SelectNode* op, std::ostream& os) final;     // 
NOLINT(*)
   void VisitExpr_(const BroadcastNode* op, std::ostream& os) final;  // 
NOLINT(*)
   void VisitExpr_(const CallNode* op, std::ostream& os) final;       // 
NOLINT(*)
   void VisitExpr_(const FloatImmNode* op, std::ostream& os) final;   // 
NOLINT(*)
diff --git a/src/tir/transforms/flatten_buffer.cc 
b/src/tir/transforms/flatten_buffer.cc
index 5a248dfbc3..933d5eeefb 100644
--- a/src/tir/transforms/flatten_buffer.cc
+++ b/src/tir/transforms/flatten_buffer.cc
@@ -51,6 +51,8 @@ class BufferFlattener : public arith::IRMutatorWithAnalyzer {
   }
 
  private:
+  using IRMutatorWithAnalyzer::VisitExpr;
+  using IRMutatorWithAnalyzer::VisitExpr_;
   using IRMutatorWithAnalyzer::VisitStmt;
   using IRMutatorWithAnalyzer::VisitStmt_;
 
diff --git a/src/tir/transforms/lower_intrin.cc 
b/src/tir/transforms/lower_intrin.cc
index 4cffe2a19d..212ccf6e56 100644
--- a/src/tir/transforms/lower_intrin.cc
+++ b/src/tir/transforms/lower_intrin.cc
@@ -119,7 +119,8 @@ class IntrinInjecter : public 
tvm::arith::IRMutatorWithAnalyzer {
       // in terms of truncdiv using only positive operands.
       arith::ConstIntBound const_int_bound = analyzer_->const_int_bound(op->a);
       if (const_int_bound->min_value < 0 &&
-          const_int_bound->min_value > 
-(Downcast<IntImm>(tvm::max_value(op->a->dtype))->value)) {
+          const_int_bound->min_value >
+              
-(Downcast<IntImm>(tvm::max_value(op->a->dtype.element_of()))->value)) {
         // The goal is to write floordiv(a,b) in terms of truncdiv, without 
using
         // negative operands.
         //
@@ -150,7 +151,7 @@ class IntrinInjecter : public 
tvm::arith::IRMutatorWithAnalyzer {
         //   floordiv(a,b)
         //     == floordiv(a + b*c, b) - c
         //     == truncdiv(a + b*c, b) - c
-        IntImm min(op->a->dtype, const_int_bound->min_value);
+        IntImm min(op->a->dtype.element_of(), const_int_bound->min_value);
         PrimExpr ceildiv = truncdiv((op->b - 1) - min, op->b);
         PrimExpr offset_numerator = analyzer_->Simplify(op->a + op->b * 
ceildiv);
         return truncdiv(offset_numerator, op->b) - ceildiv;
@@ -214,7 +215,8 @@ class IntrinInjecter : public 
tvm::arith::IRMutatorWithAnalyzer {
       // in terms of truncmod using only positive operands.
       arith::ConstIntBound const_int_bound = analyzer_->const_int_bound(op->a);
       if (const_int_bound->min_value < 0 &&
-          const_int_bound->min_value > 
-(Downcast<IntImm>(tvm::max_value(op->a->dtype))->value)) {
+          const_int_bound->min_value >
+              
-(Downcast<IntImm>(tvm::max_value(op->a->dtype.element_of()))->value)) {
         // The goal is to write floormod(a,b) in terms of truncdiv and 
truncmod,
         // without using negative operands.
         //
@@ -244,7 +246,7 @@ class IntrinInjecter : public 
tvm::arith::IRMutatorWithAnalyzer {
         //   floormod(a,b)
         //     == floormod(a + b*c, b)
         //     == truncmod(a + b*c, b)
-        IntImm min(op->a->dtype, const_int_bound->min_value);
+        IntImm min(op->a->dtype.element_of(), const_int_bound->min_value);
         PrimExpr ceildiv = truncdiv(-min + (op->b - 1), op->b);
         PrimExpr offset_numerator = analyzer_->Simplify(op->a + op->b * 
ceildiv);
         return truncmod(offset_numerator, op->b);
diff --git a/tests/python/unittest/test_arith_const_int_bound.py 
b/tests/python/unittest/test_arith_const_int_bound.py
index a97345da1a..d9ea36206b 100644
--- a/tests/python/unittest/test_arith_const_int_bound.py
+++ b/tests/python/unittest/test_arith_const_int_bound.py
@@ -349,5 +349,23 @@ def test_multiple_condition():
     assert bound.min_value == 0
 
 
+def test_broadcast_bound():
+    analyzer = tvm.arith.Analyzer()
+    a = te.var("a")
+    analyzer.update(a, tvm.arith.ConstIntBound(0, 128))
+    bound = analyzer.const_int_bound(tvm.tir.Broadcast(a, 4))
+    assert bound.min_value == 0
+    assert bound.max_value == 128
+
+
+def test_ramp_bound():
+    analyzer = tvm.arith.Analyzer()
+    a = te.var("a")
+    analyzer.update(a, tvm.arith.ConstIntBound(0, 128))
+    bound = analyzer.const_int_bound(tvm.tir.Ramp(a, 2, 4) + 2)
+    assert bound.min_value == 2
+    assert bound.max_value == 128 + 2 * 3 + 2
+
+
 if __name__ == "__main__":
     tvm.testing.main()
diff --git a/tests/python/unittest/test_target_codegen_metal.py 
b/tests/python/unittest/test_target_codegen_metal.py
index 27d0c037ed..3b1cdb4422 100644
--- a/tests/python/unittest/test_target_codegen_metal.py
+++ b/tests/python/unittest/test_target_codegen_metal.py
@@ -18,15 +18,10 @@ import tvm
 from tvm import te
 import numpy as np
 
-from tvm.contrib.nvcc import have_fp16, have_int8, have_bf16
-from tvm.contrib import nvcc
 import tvm.testing
 import tvm.script
 from tvm.script import tir as T
 
-tx = te.thread_axis("threadIdx.x")
-bx = te.thread_axis("blockIdx.x")
-
 
 @tvm.testing.requires_gpu
 @tvm.testing.requires_metal
@@ -37,9 +32,11 @@ def test_metal_inf_nan():
         A = te.placeholder((n,), name="A", dtype=dtype)
         inf_value = tvm.tir.const(value, dtype=dtype)
         C = te.compute((n,), lambda i: inf_value, name="C")
-        s = te.create_schedule(C.op)
-        s[C].bind(s[C].op.axis[0], tx)
-        fun = tvm.build(s, [A, C], target)
+        prim_func = te.create_prim_func([A, C])
+        sch = tvm.tir.Schedule(prim_func)
+        (x,) = sch.get_loops(sch.get_block("C"))
+        sch.bind(x, "threadIdx.x")
+        fun = tvm.build(sch.mod, target=target)
         a = tvm.nd.empty((n,), A.dtype, dev)
         c = tvm.nd.empty((n,), A.dtype, dev)
         # Only need to test compiling here
@@ -88,9 +85,11 @@ def test_metal_erf():
     def check_erf(dev, n, dtype):
         A = te.placeholder((n,), name="A", dtype=dtype)
         C = te.compute(A.shape, lambda *i: te.erf(A(*i)), name="C")
-        s = te.create_schedule(C.op)
-        s[C].bind(s[C].op.axis[0], tx)
-        fun = tvm.build(s, [A, C], target)
+        func = te.create_prim_func([A, C])
+        sch = tvm.tir.Schedule(func)
+        (x,) = sch.get_loops(sch.get_block("C"))
+        sch.bind(x, "threadIdx.x")
+        fun = tvm.build(sch.mod, target=target)
         a = tvm.nd.empty((n,), A.dtype, dev)
         c = tvm.nd.empty((n,), A.dtype, dev)
         # Only need to test compiling here
@@ -125,6 +124,31 @@ def test_ramp():
     assert tuple(a_nd.numpy()[0, :]) == (0, 3)
 
 
[email protected]_gpu
[email protected]_metal
+def test_select_vectorize():
+    @tvm.script.ir_module
+    class IRModule:
+        @T.prim_func
+        def main(A: T.Buffer((6), "float32"), B: T.Buffer((6,), "float32")):
+            T.func_attr({"global_symbol": "main"})
+            for i0_1 in T.thread_binding(3, thread="threadIdx.x"):
+                for i0_0 in T.vectorized(2):
+                    with T.block("block"):
+                        vi0 = T.axis.spatial(6, i0_0 * 3 + i0_1)
+                        B[vi0] = T.Select((vi0 % 2) == 0, A[vi0], T.float32(0))
+
+    target = "metal"
+    dev = tvm.metal()
+    a = np.arange(6).astype("float32")
+    a_nd = tvm.nd.array(a, dev)
+    b_nd = tvm.nd.empty((6,), "float32", dev)
+    f = tvm.build(IRModule, target=target)
+    f(a_nd, b_nd)
+    a.reshape(3, 2)[:, 1] = 0
+    np.testing.assert_allclose(b_nd.numpy(), a, atol=1e-5, rtol=1e-5)
+
+
 if __name__ == "__main__":
     test_ramp()
     test_metal_inf_nan()

Reply via email to