IVGCVSW-7924 Add TILE to Support Library (SL)

 * Add serialize parameters so that the multiples appear in the dot file

Signed-off-by: Teresa Charlin <teresa.charlinreyes@arm.com>
Change-Id: Id02ed709c97b866dabefed655b06bdb1b20c9026
diff --git a/shim/sl/canonical/Converter.cpp b/shim/sl/canonical/Converter.cpp
index 5b8c450..8200050 100644
--- a/shim/sl/canonical/Converter.cpp
+++ b/shim/sl/canonical/Converter.cpp
@@ -170,6 +170,8 @@
             return ConvertStridedSlice(operation, model, data);
         case OperationType::SUB:
             return ConvertElementwiseBinary(operation, model, data, BinaryOperation::Sub);
+        case OperationType::TILE:
+            return ConvertTile(operation, model, data);
         case OperationType::TRANSPOSE:
             return ConvertTranspose(operation, model, data);
         case OperationType::TRANSPOSE_CONV_2D:
@@ -5135,6 +5137,74 @@
     return ConvertToActivation(operation, __func__, desc, model, data);
 }
 
+bool Converter::ConvertTile(const Operation& operation, const Model& model, ConversionData& data)
+{
+    VLOG(DRIVER) << "Converter::ConvertTile()";
+
+    LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
+    if (!input.IsValid())
+    {
+        return Fail("%s: Operation has invalid inputs", __func__);
+    }
+    const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
+
+    const Operand* outputOperand = GetOutputOperand(operation, 0, model);
+    if (!outputOperand)
+    {
+        return Fail("%s: Operation has no outputs", __func__);
+    }
+    const TensorInfo& outputInfo = GetTensorInfoForOperand(*outputOperand);
+
+    const Operand* multiplesOperand = GetInputOperand(operation, 1, model);
+    if (!multiplesOperand)
+    {
+        return Fail("%s: Could not read input 1", __func__);
+    }
+    std::vector<int32_t> multiples;
+    if (!GetTensorInt32Values(*multiplesOperand, multiples, model, data))
+    {
+        return Fail("%s: Input 1 has invalid values", __func__);
+    }
+
+    TileDescriptor descriptor;
+    descriptor.m_Multiples.assign(multiples.begin(), multiples.end());
+
+    bool isSupported = false;
+    armnn::BackendId setBackend;
+    auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
+    {
+        FORWARD_LAYER_SUPPORT_FUNC(__func__,
+                                   IsTileSupported,
+                                   data.m_Backends,
+                                   isSupported,
+                                   setBackend,
+                                   inputInfo,
+                                   outputInfo,
+                                   descriptor);
+    };
+
+    if(IsDynamicTensor(outputInfo))
+    {
+        isSupported = AreDynamicTensorsSupported();
+    }
+    else
+    {
+        validateFunc(outputInfo, isSupported);
+    }
+
+    if (!isSupported)
+    {
+        return false;
+    }
+
+    IConnectableLayer* const layer = data.m_Network->AddTileLayer(descriptor);
+    layer->SetBackendId(setBackend);
+    assert(layer != nullptr);
+    input.Connect(layer->GetInputSlot(0));
+
+    return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data, nullptr, validateFunc);
+}
+
 bool Converter::ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data)
 {
     VLOG(DRIVER) << "Converter::ConvertTransposeConv2d()";
diff --git a/shim/sl/canonical/Converter.hpp b/shim/sl/canonical/Converter.hpp
index d19498d..143dd02 100644
--- a/shim/sl/canonical/Converter.hpp
+++ b/shim/sl/canonical/Converter.hpp
@@ -153,6 +153,8 @@
 
     static bool ConvertTanH(const Operation& operation, const Model& model, ConversionData& data);
 
+    static bool ConvertTile(const Operation& operation, const Model& model, ConversionData& data);
+
     static bool ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data);
 
     static bool ConvertTransposeConv2d(const Operation& operation, const Model& model, ConversionData& data);