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

manupa 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 a82abd4  [microNPU] Add support for unary elementwise CLZ (#9577)
a82abd4 is described below

commit a82abd44275273c75d2430321a091cf71835c561
Author: Elen Kalda <[email protected]>
AuthorDate: Fri Nov 26 16:27:58 2021 +0000

    [microNPU] Add support for unary elementwise CLZ (#9577)
    
    Add support for the CLZ (count leading zeros) operator
    and the codegen test.
    
    
    Co-authored-by: Rishabh Jain <[email protected]>
---
 .../backend/contrib/ethosu/op/unary_elementwise.py |  3 +-
 .../backend/contrib/ethosu/te/unary_elementwise.py |  7 ++++-
 .../contrib/ethosu/tir/unary_elementwise.py        |  4 +++
 .../backend/contrib/ethosu/tir_to_cs_translator.py |  2 ++
 src/relay/op/contrib/ethosu/unary_elementwise.cc   | 27 +++++++++++------
 tests/python/contrib/test_ethosu/infra.py          |  3 +-
 tests/python/contrib/test_ethosu/test_codegen.py   | 34 ++++++++++++++++++++++
 .../test_ethosu/test_replace_unary_elementwise.py  |  9 +++---
 .../contrib/test_ethosu/test_type_inference.py     |  6 ++--
 9 files changed, 77 insertions(+), 18 deletions(-)

diff --git a/python/tvm/relay/backend/contrib/ethosu/op/unary_elementwise.py 
b/python/tvm/relay/backend/contrib/ethosu/op/unary_elementwise.py
index a339561..35104da 100644
--- a/python/tvm/relay/backend/contrib/ethosu/op/unary_elementwise.py
+++ b/python/tvm/relay/backend/contrib/ethosu/op/unary_elementwise.py
@@ -109,6 +109,7 @@ def ethosu_unary_elementwise(
     operator_type: str
         The type of the unary elementwise operator.
             "ABS"
+            "CLZ"
     ifm_scale : float
         The quantization scale for the Input Feature Map tensor.
     ifm_zero_point : int
@@ -143,7 +144,7 @@ def ethosu_unary_elementwise(
     Returns
     -------
     out : tvm.relay.Call
-        A call to the ethosu_binary_elementwise op.
+        A call to the ethosu_unary_elementwise op.
     """
     return _make.ethosu_unary_elementwise(
         ifm,
diff --git a/python/tvm/relay/backend/contrib/ethosu/te/unary_elementwise.py 
b/python/tvm/relay/backend/contrib/ethosu/te/unary_elementwise.py
index d45a8f4..0aefc1c 100644
--- a/python/tvm/relay/backend/contrib/ethosu/te/unary_elementwise.py
+++ b/python/tvm/relay/backend/contrib/ethosu/te/unary_elementwise.py
@@ -48,6 +48,7 @@ def unary_elementwise_compute(
     operator_type: str
         The type of the unary elementwise operator.
             "ABS"
+            "CLZ"
     ifm_scale : float
         The quantization scale for the Input Feature Map tensor.
     ifm_zero_point : int
@@ -111,7 +112,11 @@ def unary_elementwise_compute(
         "rounding_mode": rounding_mode,
     }
 
-    operators = {"ABS": te.abs}
+    def clz_imp(inp):
+        # Assuming that it's a 32 bit int
+        return 32 - te.log2(inp)
+
+    operators = {"ABS": te.abs, "CLZ": clz_imp}
 
     unary_elementwise = te.compute(
         (1, ofm_height, ofm_width, ofm_channels),
diff --git a/python/tvm/relay/backend/contrib/ethosu/tir/unary_elementwise.py 
b/python/tvm/relay/backend/contrib/ethosu/tir/unary_elementwise.py
index 6dc801f..4910330 100644
--- a/python/tvm/relay/backend/contrib/ethosu/tir/unary_elementwise.py
+++ b/python/tvm/relay/backend/contrib/ethosu/tir/unary_elementwise.py
@@ -52,7 +52,11 @@ def get_unary_elementwise_params(stmt, producers, consumers):
     _, _, _, _, _, inner = get_outer_loops(body, "NHWC")
     input_pointer = None
     if isinstance(inner.value, tir.expr.Select):
+        # ABS
         input_pointer = inner.value.condition.b.buffer_var
+    if isinstance(inner.value, tir.expr.Sub):
+        # CLZ
+        input_pointer = inner.value.b.args[0].buffer_var
     output_pointer = inner.buffer_var
     # Get feature map info
     serial_ifm, _ = get_ifm_params(input_pointer, producers)
diff --git a/python/tvm/relay/backend/contrib/ethosu/tir_to_cs_translator.py 
b/python/tvm/relay/backend/contrib/ethosu/tir_to_cs_translator.py
index d276417..4e84feb 100644
--- a/python/tvm/relay/backend/contrib/ethosu/tir_to_cs_translator.py
+++ b/python/tvm/relay/backend/contrib/ethosu/tir_to_cs_translator.py
@@ -799,6 +799,8 @@ def 
_create_npu_op_unary_elementwise(serial_unary_elementwise):
     operator_type = serial_unary_elementwise.operator_type
     if operator_type == "ABS":
         op = vapi.NpuElementWiseOp.ABS
+    if operator_type == "CLZ":
+        op = vapi.NpuElementWiseOp.CLZ
 
     npu_unary_elementwise_op = vapi.NpuElementWiseOperation(op)
     npu_unary_elementwise_op.ifm = 
_create_npu_feature_map(serial_unary_elementwise.ifm)
diff --git a/src/relay/op/contrib/ethosu/unary_elementwise.cc 
b/src/relay/op/contrib/ethosu/unary_elementwise.cc
index 60f1eef..9dc07e0 100644
--- a/src/relay/op/contrib/ethosu/unary_elementwise.cc
+++ b/src/relay/op/contrib/ethosu/unary_elementwise.cc
@@ -50,7 +50,8 @@ struct EthosuUnaryElementwiseAttrs : public 
tvm::AttrsNode<EthosuUnaryElementwis
     TVM_ATTR_FIELD(operator_type)
         .describe(
             "The type of the unary elementwise operator."
-            "'ABS'");
+            "'ABS'"
+            "'CLZ'");
     TVM_ATTR_FIELD(ifm_scale).describe("The quantization scale for the Input 
Feature Map tensor.");
     TVM_ATTR_FIELD(ifm_zero_point)
         .describe("The quantization zero point for the Input Feature Map 
tensor.");
@@ -104,20 +105,28 @@ bool EthosuUnaryElementwiseRel(const Array<Type>& types, 
int num_inputs, const A
   CHECK(param != nullptr) << "EthosuUnaryElementwiseAttrs cannot be nullptr.";
 
   String operator_type = param->operator_type;
-  if (operator_type != "ABS") {
-    reporter->GetDiagCtx().EmitFatal(
-        Diagnostic::Error(reporter->GetSpan())
-        << "Invalid operator: expected ethosu_unary_elementwise 'ABS' for 
operator_type but was"
-        << operator_type);
+  if (operator_type != "ABS" && operator_type != "CLZ") {
+    reporter->GetDiagCtx().EmitFatal(Diagnostic::Error(reporter->GetSpan())
+                                     << "Invalid operator: expected 
ethosu_unary_elementwise 'ABS' "
+                                        "or 'CLZ' for operator_type but was"
+                                     << operator_type);
     return false;
   }
 
   auto ifm_dtype = ifm->dtype;
-  if (ifm_dtype != DataType::UInt(8) && ifm_dtype != DataType::Int(8)) {
+  if (ifm_dtype != DataType::UInt(8) && ifm_dtype != DataType::Int(8) && 
operator_type == "ABS") {
+    reporter->GetDiagCtx().EmitFatal(Diagnostic::Error(reporter->GetSpan())
+                                     << "Invalid operator: expected 
ethosu_unary_elementwise "
+                                     << operator_type << "input data type "
+                                     << "of type(uint8) or type(int8) but was 
" << ifm_dtype);
+    return false;
+  }
+
+  if (ifm_dtype != DataType::Int(32) && operator_type == "CLZ") {
     reporter->GetDiagCtx().EmitFatal(
         Diagnostic::Error(reporter->GetSpan())
-        << "Invalid operator: expected ethosu_unary_elementwise input data 
type "
-        << "of type(uint8) or type(int8) but was " << ifm_dtype);
+        << "Invalid operator: expected ethosu_unary_elementwise CLZ input data 
type "
+        << "of type(int32) but was " << ifm_dtype);
     return false;
   }
 
diff --git a/tests/python/contrib/test_ethosu/infra.py 
b/tests/python/contrib/test_ethosu/infra.py
index 38bd88c..5f33926 100644
--- a/tests/python/contrib/test_ethosu/infra.py
+++ b/tests/python/contrib/test_ethosu/infra.py
@@ -312,8 +312,9 @@ def make_partitioned_function(relay_op):
 
     ifm0 = relay.analysis.free_vars(relay_op)
     ifm_shape = ifm0[0].type_annotation.shape
+    ifm_dtype = ifm0[0].type_annotation.dtype
 
-    ifm = relay.var("ifm", shape=ifm_shape, dtype="int8")
+    ifm = relay.var("ifm", shape=ifm_shape, dtype=ifm_dtype)
 
     glb_ethosu = relay.GlobalVar("tvmgen_default_ethosu_main_0")
 
diff --git a/tests/python/contrib/test_ethosu/test_codegen.py 
b/tests/python/contrib/test_ethosu/test_codegen.py
index 92a1ad7..b6cf873 100644
--- a/tests/python/contrib/test_ethosu/test_codegen.py
+++ b/tests/python/contrib/test_ethosu/test_codegen.py
@@ -969,5 +969,39 @@ def test_ethosu_section_name():
     assert '__attribute__((section(".rodata.tvm"), aligned(16))) static int8_t 
weights' in source
 
 
[email protected]("accel_type", ACCEL_TYPES)
+def test_ethosu_clz(accel_type):
+    ifm_shape = (1, 42, 5, 4)
+    # Create a "partitioned" Relay function
+    ifm0 = relay.var("ifm0", shape=ifm_shape, dtype="int32")
+    clz = infra.make_ethosu_unary_elementwise(ifm0, 4, "CLZ")
+    mod = infra.make_partitioned_function(clz)
+
+    in_data = np.random.randint(-500000, high=500000, size=ifm_shape, 
dtype="int32")
+
+    def clz_comp(n):
+        n_bin = np.binary_repr(n)
+        if n_bin[0] == "-":
+            return 0
+        else:
+            return 32 - len(n_bin)
+
+    out_data = np.array([clz_comp(i) for i in 
in_data.ravel()]).reshape(ifm_shape).astype("int32")
+
+    compiled_model = infra.build_source(mod, {"ifm": in_data}, [out_data], 
accel_type)
+
+    imported_modules = compiled_model[0].executor_factory.lib.imported_modules
+    assert len(imported_modules) == 2
+    ethosu_module = imported_modules[0]
+
+    # Verify generated C source
+    get_cs = tvm._ffi.get_global_func("runtime.module.ethos-u.getcs")
+    cmms = get_cs(ethosu_module)
+    cmms = bytes.fromhex(cmms)
+
+    infra.print_payload(cmms)
+    infra.verify_source(compiled_model, accel_type)
+
+
 if __name__ == "__main__":
     pytest.main([__file__])
diff --git a/tests/python/contrib/test_ethosu/test_replace_unary_elementwise.py 
b/tests/python/contrib/test_ethosu/test_replace_unary_elementwise.py
index eff81c4..e1c633e 100644
--- a/tests/python/contrib/test_ethosu/test_replace_unary_elementwise.py
+++ b/tests/python/contrib/test_ethosu/test_replace_unary_elementwise.py
@@ -50,7 +50,7 @@ def _get_unary_elementwise_args(call, include_buffers=False, 
remove_constants=Fa
         ((1, 8, 9, 40), 40, "NHWC", "NHCWB16", "TFL"),
     ],
 )
[email protected]("operator_type", ["ABS"])
[email protected]("operator_type, data_type", [("ABS", "int8"), ("CLZ", 
"int32")])
 @pytest.mark.parametrize("activation", ["NONE"])
 def test_unary_elementwise_single(
     ifm_shape,
@@ -60,8 +60,9 @@ def test_unary_elementwise_single(
     rounding_mode,
     operator_type,
     activation,
+    data_type,
 ):
-    ifm = relay.var("ifm", shape=ifm_shape, dtype="int8")
+    ifm = relay.var("ifm", shape=ifm_shape, dtype=data_type)
 
     unary_elementwise = make_ethosu_unary_elementwise(
         ifm, ifm_channels, operator_type, activation, ifm_layout, ofm_layout, 
rounding_mode
@@ -102,7 +103,7 @@ def test_unary_elementwise_single(
 
     serial_unary_elementwise = spec.SerialUnaryElementwise(
         ifm=spec.SerialFeatureMap(
-            data_type="int8",
+            data_type=data_type,
             height=ifm_shape[1],
             width=ifm_shape[2] if ifm_layout == "NHWC" else ifm_shape[3],
             channels=ifm_channels,
@@ -121,7 +122,7 @@ def test_unary_elementwise_single(
             stride_c=ifm_stride_c,
         ),
         ofm=spec.SerialFeatureMap(
-            data_type="int8",
+            data_type=data_type,
             height=ofm_height,
             width=ofm_width,
             channels=ifm_channels,
diff --git a/tests/python/contrib/test_ethosu/test_type_inference.py 
b/tests/python/contrib/test_ethosu/test_type_inference.py
index 778e4ef..9b60656 100644
--- a/tests/python/contrib/test_ethosu/test_type_inference.py
+++ b/tests/python/contrib/test_ethosu/test_type_inference.py
@@ -381,14 +381,16 @@ def test_ethosu_identity_invalid_dtype():
 @pytest.mark.parametrize(
     "ofm_shape, ofm_layout", [((1, 4, 5, 33), "NHWC"), ((1, 4, 3, 5, 16), 
"NHCWB16")]
 )
[email protected]("operator_type, data_type", [("ABS", "int8"), ("CLZ", 
"int32")])
 def test_ethosu_unary_elementwise_type_inference(
     ifm_shape,
     ifm_layout,
     ofm_shape,
     ofm_layout,
+    operator_type,
+    data_type,
 ):
-    ifm = relay.var("ifm", shape=ifm_shape, dtype="int8")
-    operator_type = "ABS"
+    ifm = relay.var("ifm", shape=ifm_shape, dtype=data_type)
     ofm_channels = 33
     unary_elementwise = make_ethosu_unary_elementwise(
         ifm,

Reply via email to