COMPMID-632 Assembly: Integrate gemmlowp assembly version

Integrate generic gemmlowp assembly version for u8.

Change-Id: I17ed4494c25a132b2bac581febe1544e49b4f352
Reviewed-on: https://eu-gerrit-1.euhpc.arm.com/110114
Tested-by: BSG Visual Compute Jenkins server to access repositories on http://mpd-gerrit.cambridge.arm.com <bsgcomp@arm.com>
Reviewed-by: Pablo Tello <pablo.tello@arm.com>
diff --git a/tests/validation/CPP/GEMMLowp.cpp b/tests/validation/CPP/GEMMLowp.cpp
index bf002cf..35b8a64 100644
--- a/tests/validation/CPP/GEMMLowp.cpp
+++ b/tests/validation/CPP/GEMMLowp.cpp
@@ -63,19 +63,21 @@
 }
 } // namespace
 
-template <typename T>
-SimpleTensor<int32_t> gemmlowp_matrix_multiply_core(const SimpleTensor<T> &a, const SimpleTensor<T> &b, int32_t a_offset, int32_t b_offset)
+template <typename T_out, typename T_in>
+SimpleTensor<T_out> gemmlowp_matrix_multiply_core(const SimpleTensor<T_in> &a, const SimpleTensor<T_in> &b, int32_t a_offset, int32_t b_offset)
 {
-    TensorShape shape(b.shape()[0], a.shape()[1]);
+    static_assert(std::is_same<typename std::decay<T_out>::type, int32_t>::value, "Only int32_t is allowed for the output");
 
-    SimpleTensor<int32_t> c(shape, DataType::S32);
+    TensorShape         shape(b.shape()[0], a.shape()[1]);
+    DataType            dt = std::is_same<T_out, int32_t>::value ? DataType::S32 : DataType::U32;
+    SimpleTensor<T_out> c(shape, dt);
 
     const int K       = a.shape().x();
     const int b_width = b.shape().x();
     const int rows    = c.shape().y(); //M
     const int cols    = c.shape().x(); //N
 
-    std::vector<int32_t> acc;
+    std::vector<T_out> acc;
     acc.resize(cols);
 
     for(int i = 0; i < rows; ++i)
@@ -86,11 +88,11 @@
         }
         for(int k = 0; k < K; ++k)
         {
-            const int32_t tmp_a = a_offset + static_cast<int32_t>(a[k + i * K]);
+            const T_out tmp_a = a_offset + static_cast<T_out>(a[k + i * K]);
             for(int j = 0; j < b_width; ++j)
             {
-                const int32_t tmp_b       = b_offset + static_cast<int32_t>(b[j + k * b_width]);
-                const int32_t mult_as_int = tmp_a * tmp_b;
+                const T_out tmp_b       = b_offset + static_cast<T_out>(b[j + k * b_width]);
+                const T_out mult_as_int = tmp_a * tmp_b;
                 acc[j] += mult_as_int;
             }
         }
@@ -104,9 +106,10 @@
 }
 
 // used to validate assembly kernels which don't know anything about offsets
-SimpleTensor<int32_t> gemmlowp(const SimpleTensor<int8_t> &a, const SimpleTensor<int8_t> &b)
+template <typename T1, typename T2>
+SimpleTensor<T1> gemmlowp(const SimpleTensor<T2> &a, const SimpleTensor<T2> &b)
 {
-    return gemmlowp_matrix_multiply_core(a, b, 0, 0);
+    return gemmlowp_matrix_multiply_core<T1, T2>(a, b, 0, 0);
 }
 
 template <typename T>
@@ -130,11 +133,14 @@
     return dst;
 }
 
-template SimpleTensor<int32_t> gemmlowp_matrix_multiply_core(const SimpleTensor<uint8_t> &a, const SimpleTensor<uint8_t> &b, int32_t a_offset, int32_t b_offset);
 template SimpleTensor<uint8_t> gemmlowp_quantize_down_int32_to_uint8_scale(const SimpleTensor<int32_t> &a, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min,
                                                                            int32_t max);
 template SimpleTensor<uint8_t> gemmlowp_quantize_down_int32_to_uint8_scale(const SimpleTensor<int32_t> &a, const SimpleTensor<int32_t> &b, int32_t result_offset, int32_t result_mult_int,
                                                                            int32_t result_shift, int32_t min, int32_t max);
+template SimpleTensor<int32_t> gemmlowp_matrix_multiply_core(const SimpleTensor<int8_t> &a, const SimpleTensor<int8_t> &b, int32_t a_offset, int32_t b_offset);
+template SimpleTensor<int32_t> gemmlowp_matrix_multiply_core(const SimpleTensor<uint8_t> &a, const SimpleTensor<uint8_t> &b, int32_t a_offset, int32_t b_offset);
+template SimpleTensor<int32_t> gemmlowp(const SimpleTensor<int8_t> &a, const SimpleTensor<int8_t> &b);
+template SimpleTensor<int32_t> gemmlowp(const SimpleTensor<uint8_t> &a, const SimpleTensor<uint8_t> &b);
 } // namespace reference
 } // namespace validation
 } // namespace test
diff --git a/tests/validation/CPP/GEMMLowp.h b/tests/validation/CPP/GEMMLowp.h
index ee33d8e..6c72b56 100644
--- a/tests/validation/CPP/GEMMLowp.h
+++ b/tests/validation/CPP/GEMMLowp.h
@@ -35,13 +35,16 @@
 {
 namespace reference
 {
-SimpleTensor<int32_t> gemmlowp(const SimpleTensor<int8_t> &a, const SimpleTensor<int8_t> &b);
-
-template <typename T>
-SimpleTensor<int32_t> gemmlowp_matrix_multiply_core(const SimpleTensor<T> &a, const SimpleTensor<T> &b, int32_t a_offset, int32_t b_offset);
-
 template <typename T>
 SimpleTensor<uint8_t> gemmlowp_quantize_down_int32_to_uint8_scale(const SimpleTensor<T> &in, int32_t result_offset, int32_t result_mult_int, int32_t result_shift, int32_t min = 0, int32_t max = 0);
+template <typename T1, typename T2>
+SimpleTensor<T1> gemmlowp_matrix_multiply_core(const SimpleTensor<T2> &a, const SimpleTensor<T2> &b, int32_t a_offset, int32_t b_offset);
+
+template <typename T>
+SimpleTensor<uint8_t> gemmlowp_quantize_down_int32_to_uint8_scale(const SimpleTensor<T> &in, int32_t result_offset, int32_t result_mult_int, int32_t result_shift);
+
+template <typename T1, typename T2>
+SimpleTensor<T1> gemmlowp(const SimpleTensor<T2> &a, const SimpleTensor<T2> &b);
 
 template <typename T>
 SimpleTensor<uint8_t> gemmlowp_quantize_down_int32_to_uint8_scale(const SimpleTensor<T> &in, const SimpleTensor<T> &bias, int32_t result_offset, int32_t result_mult_int, int32_t result_shift,
diff --git a/tests/validation/NEON/GEMMLowp.cpp b/tests/validation/NEON/GEMMLowp.cpp
index 1418578..6366223 100644
--- a/tests/validation/NEON/GEMMLowp.cpp
+++ b/tests/validation/NEON/GEMMLowp.cpp
@@ -58,14 +58,27 @@
 
 TEST_SUITE(NEON)
 TEST_SUITE(ASSEMBLY_MATRIX_MULTIPLY)
-using NEGEMMAssemblyFixture = GEMMLowpAssemblyFixture<Tensor, Accessor, NEGEMMLowpAssemblyMatrixMultiplyCore>;
-FIXTURE_DATA_TEST_CASE(RunSmall, NEGEMMAssemblyFixture, framework::DatasetMode::PRECOMMIT, data_matrix_multiply)
+
+using NEGEMMAssemblyFixture_S8 = GEMMLowpAssemblyFixture<Tensor, Accessor, NEGEMMLowpAssemblyMatrixMultiplyCore, int8_t>;
+using NEGEMMAssemblyFixture_U8 = GEMMLowpAssemblyFixture<Tensor, Accessor, NEGEMMLowpAssemblyMatrixMultiplyCore, uint8_t>;
+
+TEST_SUITE(S8)
+FIXTURE_DATA_TEST_CASE(RunSmall, NEGEMMAssemblyFixture_S8, framework::DatasetMode::PRECOMMIT, data_matrix_multiply)
 {
     // Validate output
     validate(Accessor(_target), _reference);
 }
 TEST_SUITE_END()
 
+TEST_SUITE(U8)
+FIXTURE_DATA_TEST_CASE(RunSmall, NEGEMMAssemblyFixture_U8, framework::DatasetMode::PRECOMMIT, data_matrix_multiply)
+{
+    // Validate output
+    validate(Accessor(_target), _reference);
+}
+TEST_SUITE_END()
+TEST_SUITE_END()
+
 TEST_SUITE(GEMMLowp)
 
 TEST_SUITE(INTERLEAVE_BLOCKED)
diff --git a/tests/validation/fixtures/GEMMLowpAssemblyFixture.h b/tests/validation/fixtures/GEMMLowpAssemblyFixture.h
index a258744..38e08f7 100644
--- a/tests/validation/fixtures/GEMMLowpAssemblyFixture.h
+++ b/tests/validation/fixtures/GEMMLowpAssemblyFixture.h
@@ -42,7 +42,7 @@
 {
 namespace validation
 {
-template <typename TensorType, typename AccessorType, typename FunctionType>
+template <typename TensorType, typename AccessorType, typename FunctionType, typename T2>
 class GEMMLowpAssemblyFixture : public framework::Fixture
 {
 public:
@@ -66,9 +66,11 @@
 
     TensorType compute_target(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_c)
     {
+        DataType dt_in = std::is_same<T2, int8_t>::value ? DataType::S8 : DataType::U8;
+
         // Create tensors
-        TensorType a = create_tensor<TensorType>(shape_a, DataType::S8, 1);
-        TensorType b = create_tensor<TensorType>(shape_b, DataType::S8, 1);
+        TensorType a = create_tensor<TensorType>(shape_a, dt_in, 1);
+        TensorType b = create_tensor<TensorType>(shape_b, dt_in, 1);
         TensorType c = create_tensor<TensorType>(shape_c, DataType::S32, 1);
 
         // Create and configure function
@@ -89,8 +91,16 @@
         ARM_COMPUTE_EXPECT(!c.info()->is_resizable(), framework::LogLevel::ERRORS);
 
         // Fill tensors
-        fill(AccessorType(a), 0, -128, 127);
-        fill(AccessorType(b), 1, -128, 127);
+        if(dt_in == DataType::S8)
+        {
+            fill(AccessorType(a), 0, -128, 127);
+            fill(AccessorType(b), 1, -128, 127);
+        }
+        else
+        {
+            fill(AccessorType(a), 0, 0, 128);
+            fill(AccessorType(b), 1, 0, 128);
+        }
         fill(AccessorType(c), 2, 0, 0);
 
         // Compute GEMM function
@@ -100,15 +110,25 @@
 
     SimpleTensor<int32_t> compute_reference(const TensorShape &shape_a, const TensorShape &shape_b, const TensorShape &shape_c)
     {
+        DataType dt = std::is_same<T2, int8_t>::value ? DataType::S8 : DataType::U8;
+
         // Create reference
-        SimpleTensor<int8_t> a{ shape_a, DataType::S8, 1 };
-        SimpleTensor<int8_t> b{ shape_b, DataType::S8, 1 };
+        SimpleTensor<T2> a{ shape_a, dt, 1 };
+        SimpleTensor<T2> b{ shape_b, dt, 1 };
 
         // Fill reference
-        fill(a, 0, -128, 127);
-        fill(b, 1, -128, 127);
+        if(dt == DataType::S8)
+        {
+            fill(a, 0, -128, 127);
+            fill(b, 1, -128, 127);
+        }
+        else
+        {
+            fill(a, 0, 0, 128);
+            fill(b, 1, 0, 128);
+        }
 
-        return reference::gemmlowp(a, b);
+        return reference::gemmlowp<int32_t, T2>(a, b);
     }
 
     TensorType            _target{};
diff --git a/tests/validation/fixtures/GEMMLowpFixture.h b/tests/validation/fixtures/GEMMLowpFixture.h
index a99e932..60b89bc 100644
--- a/tests/validation/fixtures/GEMMLowpFixture.h
+++ b/tests/validation/fixtures/GEMMLowpFixture.h
@@ -110,7 +110,7 @@
         fill(a, 0);
         fill(b, 1);
 
-        return reference::gemmlowp_matrix_multiply_core<uint8_t>(a, b, a_offset, b_offset);
+        return reference::gemmlowp_matrix_multiply_core<int32_t, uint8_t>(a, b, a_offset, b_offset);
     }
 
     TensorType            _target{};