Remove ReluN conversion and implementation (#324)

The ReluN operator was removed from the TOSA spec and the MLIR op is
removed with llvm/llvm-project@9dec80b. This patch therefore drops the
conversion to EmitC as well as the reference implementation. The latter
already relied on clamp. Hence, tests are moved instead of dropped.
diff --git a/docs/tosa-op-coverage.md b/docs/tosa-op-coverage.md
index 9d49dd4..4f4e822 100644
--- a/docs/tosa-op-coverage.md
+++ b/docs/tosa-op-coverage.md
@@ -17,7 +17,6 @@
 | log                    | :heavy_check_mark: | |
 | negate                 | :heavy_check_mark: | |
 | reciprocal             | :heavy_check_mark: | |
-| reluN                  | :heavy_check_mark: | |
 | rescale                | :heavy_check_mark: | |
 | rsqrt                  | :heavy_check_mark: | |
 | tanh                   | :heavy_check_mark: | |
diff --git a/lib/Dialect/EmitC/Conversion/TosaToEmitC.cpp b/lib/Dialect/EmitC/Conversion/TosaToEmitC.cpp
index 847767b..9a9db8f 100644
--- a/lib/Dialect/EmitC/Conversion/TosaToEmitC.cpp
+++ b/lib/Dialect/EmitC/Conversion/TosaToEmitC.cpp
@@ -361,54 +361,6 @@
   }
 };
 
-/// Convert `tosa.reluN` into an `emitc.call` operation.
-class ReluNOpConversion : public OpConversionPattern<tosa::ReluNOp> {
-  using OpConversionPattern<tosa::ReluNOp>::OpConversionPattern;
-
-public:
-  ReluNOpConversion(MLIRContext *ctx)
-      : OpConversionPattern<tosa::ReluNOp>(ctx) {}
-
-private:
-  LogicalResult
-  matchAndRewrite(tosa::ReluNOp reluNOp, OpAdaptor adaptor,
-                  ConversionPatternRewriter &rewriter) const override {
-
-    StringRef funcName = "emitc::tosa::reluN";
-    StringAttr callee = rewriter.getStringAttr(funcName);
-
-    SmallVector<Attribute, 2> arguments;
-    arguments.push_back(rewriter.getIndexAttr(0));
-
-    // TOSA specifies the max attributes to be either exact i64 or f32,
-    // regardless of the operand's element type. So we need to make sure that
-    // the max attribute type match the operand's element type and it's bit
-    // width.
-    auto elementType =
-        adaptor.getInput().getType().cast<RankedTensorType>().getElementType();
-    if (elementType.isa<IntegerType>()) {
-      // Change the max_int type to the element type of the operand.
-      auto maxInt = reluNOp.getMaxInt();
-      arguments.push_back(IntegerAttr::get(elementType, maxInt));
-    } else if (elementType.isa<FloatType>()) {
-      // Change the max_fp type to the element type of the operand.
-      auto maxFp = reluNOp.getMaxFpAttr().getValueAsDouble();
-      arguments.push_back(FloatAttr::get(elementType, maxFp));
-    } else {
-      return reluNOp.emitError(
-          "Operand of tosa.reluN has to be tensor of integer or float values.");
-    }
-    ArrayAttr args = rewriter.getArrayAttr(arguments);
-    ArrayAttr templateArgs;
-
-    rewriter.replaceOpWithNewOp<emitc::CallOp>(reluNOp, reluNOp.getType(),
-                                               callee, args, templateArgs,
-                                               adaptor.getOperands());
-
-    return success();
-  }
-};
-
 /// Convert `tosa.rsqrt` into an `emitc.call` operation.
 class RsqrtOpConversion : public OpConversionPattern<tosa::RsqrtOp> {
   using OpConversionPattern<tosa::RsqrtOp>::OpConversionPattern;
@@ -807,7 +759,6 @@
   patterns.add<NegateOpConversion>(ctx);
   patterns.add<CallOpConversion<tosa::ReciprocalOp>>(ctx,
                                                      "emitc::tosa::reciprocal");
-  patterns.add<ReluNOpConversion>(ctx);
   patterns.add<RescaleOpConversion>(ctx);
   patterns.add<RsqrtOpConversion>(ctx);
   patterns.add<CallOpConversion<tosa::TanhOp>>(ctx, "emitc::tosa::tanh");
@@ -886,7 +837,6 @@
                         tosa::LogOp,
                         tosa::NegateOp,
                         tosa::ReciprocalOp,
-                        tosa::ReluNOp,
                         tosa::RescaleOp,
                         tosa::RsqrtOp,
                         tosa::TanhOp>();
diff --git a/reference-implementation/include/emitc/tosa.h b/reference-implementation/include/emitc/tosa.h
index 5a5bb68..8a1b46f 100644
--- a/reference-implementation/include/emitc/tosa.h
+++ b/reference-implementation/include/emitc/tosa.h
@@ -108,14 +108,6 @@
   return unary<Src>(x, f);
 }
 
-// ReluNOp
-template <typename Src>
-inline Src reluN(Src operand, typename Src::value_type max_value) {
-  Tensor<typename Src::value_type> min{0};
-  Tensor<typename Src::value_type> max{max_value};
-  return emitc::clamp(min, operand, max);
-}
-
 // RescaleOp
 template <typename Dest, size_t Dim, typename Src>
 inline Dest rescale(Src x, typename get_element_type<Src>::type in_zp,
diff --git a/reference-implementation/unittests/tosa.cpp b/reference-implementation/unittests/tosa.cpp
index 201c168..89357f9 100644
--- a/reference-implementation/unittests/tosa.cpp
+++ b/reference-implementation/unittests/tosa.cpp
@@ -80,6 +80,60 @@
 
     EXPECT_THAT(result, Pointwise(FloatEq(), expected_result));
   }
+
+  // These are the tests of the former ReluN operation, which has been removed
+  // from the TOSA specification. clamp with min_value=0 can be used instead.
+  {
+    Tensor0D<int32_t> operand{0};
+    int32_t max_value = 0;
+    Tensor0D<int32_t> expected_result{0};
+    Tensor0D<int32_t> result = tosa::clamp(operand, {0}, max_value);
+
+    EXPECT_THAT(result, Pointwise(Eq(), expected_result));
+  }
+  {
+    Tensor0D<int32_t> operand{0};
+    int32_t max_value = 0;
+    Tensor0D<int32_t> expected_result{0};
+    Tensor0D<int32_t> result = tosa::clamp(operand, {0}, max_value);
+
+    EXPECT_THAT(result, Pointwise(Eq(), expected_result));
+  }
+  {
+    Tensor1D<double, 2> operand{-4.7, 1.3};
+    double max_value = 1.4;
+    Tensor1D<double, 2> expected_result{0, 1.3};
+    Tensor1D<double, 2> result = tosa::clamp(operand, {0}, max_value);
+
+    EXPECT_THAT(result, Pointwise(DoubleEq(), expected_result));
+  }
+  {
+    Tensor2D<float, 2, 2> operand{0.0f, -9.9f, 4.4f, 8.8f};
+    float max_value = 5.5f;
+    Tensor2D<float, 2, 2> expected_result{0.0f, 0.0f, 4.4f, 5.5f};
+    Tensor2D<float, 2, 2> result = tosa::clamp(operand, {0}, max_value);
+
+    EXPECT_THAT(result, Pointwise(FloatEq(), expected_result));
+  }
+  {
+    Tensor3D<int64_t, 3, 2, 1> operand{4, 1, -1, 3, 0, 2};
+    int64_t max_value = 3;
+    Tensor3D<int64_t, 3, 2, 1> expected_result{3, 1, 0, 3, 0, 2};
+    Tensor3D<int64_t, 3, 2, 1> result = tosa::clamp(operand, {0}, max_value);
+
+    EXPECT_THAT(result, Pointwise(Eq(), expected_result));
+  }
+  {
+    Tensor4D<int16_t, 1, 2, 3, 2> operand{7812,  15481,  -30284, 30996,
+                                          18736, 6699,   31903,  26229,
+                                          15931, -18954, -27643, 19133};
+    int16_t max_value = 20000;
+    Tensor4D<int16_t, 1, 2, 3, 2> expected_result{
+        7812, 15481, 0, 20000, 18736, 6699, 20000, 20000, 15931, 0, 0, 19133};
+    Tensor4D<int16_t, 1, 2, 3, 2> result = tosa::clamp(operand, {0}, max_value);
+
+    EXPECT_THAT(result, Pointwise(FloatEq(), expected_result));
+  }
 }
 
 TEST(tosa, clz) {
@@ -245,52 +299,6 @@
   }
 }
 
-TEST(tosa, reluN) {
-  {
-    Tensor0D<int32_t> operand{0};
-    int32_t max_value = 0;
-    Tensor0D<int32_t> expected_result{0};
-    Tensor0D<int32_t> result = tosa::reluN(operand, max_value);
-
-    EXPECT_THAT(result, Pointwise(Eq(), expected_result));
-  }
-  {
-    Tensor1D<double, 2> operand{-4.7, 1.3};
-    double max_value = 1.4;
-    Tensor1D<double, 2> expected_result{0, 1.3};
-    Tensor1D<double, 2> result = tosa::reluN(operand, max_value);
-
-    EXPECT_THAT(result, Pointwise(DoubleEq(), expected_result));
-  }
-  {
-    Tensor2D<float, 2, 2> operand{0.0f, -9.9f, 4.4f, 8.8f};
-    float max_value = 5.5f;
-    Tensor2D<float, 2, 2> expected_result{0.0f, 0.0f, 4.4f, 5.5f};
-    Tensor2D<float, 2, 2> result = tosa::reluN(operand, max_value);
-
-    EXPECT_THAT(result, Pointwise(FloatEq(), expected_result));
-  }
-  {
-    Tensor3D<int64_t, 3, 2, 1> operand{4, 1, -1, 3, 0, 2};
-    int64_t max_value = 3;
-    Tensor3D<int64_t, 3, 2, 1> expected_result{3, 1, 0, 3, 0, 2};
-    Tensor3D<int64_t, 3, 2, 1> result = tosa::reluN(operand, max_value);
-
-    EXPECT_THAT(result, Pointwise(Eq(), expected_result));
-  }
-  {
-    Tensor4D<int16_t, 1, 2, 3, 2> operand{7812,  15481,  -30284, 30996,
-                                          18736, 6699,   31903,  26229,
-                                          15931, -18954, -27643, 19133};
-    int16_t max_value = 20000;
-    Tensor4D<int16_t, 1, 2, 3, 2> expected_result{
-        7812, 15481, 0, 20000, 18736, 6699, 20000, 20000, 15931, 0, 0, 19133};
-    Tensor4D<int16_t, 1, 2, 3, 2> result = tosa::reluN(operand, max_value);
-
-    EXPECT_THAT(result, Pointwise(FloatEq(), expected_result));
-  }
-}
-
 // Binary elementwise ops
 TEST(tosa, arithmetic_right_shift) {
   {
diff --git a/test/Conversion/tosa-to-emitc.mlir b/test/Conversion/tosa-to-emitc.mlir
index 9554201..0a9e2d4 100644
--- a/test/Conversion/tosa-to-emitc.mlir
+++ b/test/Conversion/tosa-to-emitc.mlir
@@ -326,27 +326,3 @@
   %1 = "tosa.transpose"(%arg0, %0) : (tensor<13x21x3xf32>, tensor<3xi32>) -> tensor<3x13x21xf32>
   return %1 : tensor<3x13x21xf32>
 }
-
-func.func @test_relu0(%arg0: tensor<13x21x3xf32>) -> tensor<13x21x3xf32> {
-  // CHECK: %0 = emitc.call "emitc::tosa::reluN"(%arg0) {args = [0 : index, 3.40282347E+38 : f32]} : (tensor<13x21x3xf32>) -> tensor<13x21x3xf32>
-  %0 = "tosa.reluN"(%arg0) {max_fp = 3.40282347E+38 : f32, max_int = 0 : i64} : (tensor<13x21x3xf32>) -> tensor<13x21x3xf32>
-  return %0 : tensor<13x21x3xf32>
-}
-
-func.func @test_relu1(%arg0: tensor<13x21x3xi64>) -> tensor<13x21x3xi64> {
-  // CHECK: %0 = emitc.call "emitc::tosa::reluN"(%arg0) {args = [0 : index, 255]} : (tensor<13x21x3xi64>) -> tensor<13x21x3xi64>
-  %0 = "tosa.reluN"(%arg0) {max_fp = 0.0 : f32, max_int = 255 : i64} : (tensor<13x21x3xi64>) -> tensor<13x21x3xi64>
-  return %0 : tensor<13x21x3xi64>
-}
-
-func.func @test_relu2(%arg0: tensor<13x21x3xi32>) -> tensor<13x21x3xi32> {
-  // CHECK: %0 = emitc.call "emitc::tosa::reluN"(%arg0) {args = [0 : index, 255 : i32]} : (tensor<13x21x3xi32>) -> tensor<13x21x3xi32>
-  %0 = "tosa.reluN"(%arg0) {max_fp = 0.0 : f32, max_int = 255 : i64} : (tensor<13x21x3xi32>) -> tensor<13x21x3xi32>
-  return %0 : tensor<13x21x3xi32>
-}
-
-func.func @test_relu3(%arg0: tensor<13x21x3xf16>) -> tensor<13x21x3xf16> {
-  // CHECK: %0 = emitc.call "emitc::tosa::reluN"(%arg0) {args = [0 : index, 1.500000e+00 : f16]} : (tensor<13x21x3xf16>) -> tensor<13x21x3xf16>
-  %0 = "tosa.reluN"(%arg0) {max_fp = 1.5 : f32, max_int = 0 : i64} : (tensor<13x21x3xf16>) -> tensor<13x21x3xf16>
-  return %0 : tensor<13x21x3xf16>
-}