From 9c9d5b9d796d243d88bd7a7aebb2e7e6c467e3a4 Mon Sep 17 00:00:00 2001 From: Cathal Corbett Date: Wed, 17 Aug 2022 17:30:16 +0100 Subject: IVGCVSW-7133 Add TosaMappings backbone structure with support for Addition TosaMappings operator. Signed-off-by: Cathal Corbett Change-Id: Ibea0cf625b3af4ab38e8b985f7a129c983ca9659 --- src/backends/tosaCommon/CMakeLists.txt | 25 +++++ src/backends/tosaCommon/TosaMappings.hpp | 81 ++++++++++++++ src/backends/tosaCommon/common.cmake | 11 ++ .../operatorMappings/AdditionOperator.hpp | 52 +++++++++ .../tosaCommon/operatorMappings/CMakeLists.txt | 14 +++ .../operatorMappings/TosaOperatorUtils.hpp | 60 ++++++++++ src/backends/tosaCommon/test/CMakeLists.txt | 14 +++ .../tosaCommon/test/TosaOperatorMappingTests.cpp | 124 +++++++++++++++++++++ 8 files changed, 381 insertions(+) create mode 100644 src/backends/tosaCommon/CMakeLists.txt create mode 100644 src/backends/tosaCommon/TosaMappings.hpp create mode 100644 src/backends/tosaCommon/common.cmake create mode 100644 src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp create mode 100644 src/backends/tosaCommon/operatorMappings/CMakeLists.txt create mode 100644 src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp create mode 100644 src/backends/tosaCommon/test/CMakeLists.txt create mode 100644 src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp (limited to 'src') diff --git a/src/backends/tosaCommon/CMakeLists.txt b/src/backends/tosaCommon/CMakeLists.txt new file mode 100644 index 0000000000..9653859767 --- /dev/null +++ b/src/backends/tosaCommon/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT +# + +include_directories(SYSTEM ${FLATBUFFERS_INCLUDE_PATH}) +include_directories(SYSTEM ${TOSA_SERIALIZATION_LIB_INCLUDE}) + +list(APPEND armnnTosaBackend_sources + TosaMappings.hpp + ) + +add_subdirectory(operatorMappings) + +if(BUILD_UNIT_TESTS) + add_subdirectory(test) +endif() + +add_library(armnnTosaBackend OBJECT ${armnnTosaBackend_sources}) +target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn) +target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils) +target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/backends) +target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/src/profiling) +target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/profiling/common/include) +target_include_directories(armnnTosaBackend PRIVATE ${PROJECT_SOURCE_DIR}/profiling/client/include) diff --git a/src/backends/tosaCommon/TosaMappings.hpp b/src/backends/tosaCommon/TosaMappings.hpp new file mode 100644 index 0000000000..5728ff3203 --- /dev/null +++ b/src/backends/tosaCommon/TosaMappings.hpp @@ -0,0 +1,81 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include +#include "operatorMappings/AdditionOperator.hpp" + +using namespace armnn; +using namespace tosa; + +// From the input armnn::Layer, set the corresponding data field in the +// tosa::TosaSerializationTensor where constant tensor data exists in the armnn::Layer. +void SetBasicBlockConstantTensorData(Layer* layer, TosaSerializationBasicBlock* /*basicBlock*/) +{ + switch (layer->GetType()) + { + case LayerType::Convolution2d: + { + // ToDo: using Convolution2d as an example as it has constant tensors for weights and bias. + // ToDo: manually set TosaOperator data of basicBlock where constant tensors exist. + } + default: + // If no switch statement for layer, no constant tensors exist in that layer, return + return; + } +} + +// Populates a tosa::TosaSerializationBasicBlock from constructing +// tosa::TosaSerializationOperator(s) and tosa::TosaSerializationTensor(s) +// based on the input armnn::LayerType and associated armnn::TensorInfos and armnn::Descriptor. +// +// If an armnn::LayerType does not have a tosa mapping or the mapping is not implemented in ArmNN, +// an empty tosa::TosaSerializationBasicBlock() is returned with operator tosa::Op_UNKNOWN. +TosaSerializationBasicBlock* GetTosaMapping(const LayerType type, + const std::vector& inputs, + const std::vector& outputs, + const BaseDescriptor& /*descriptor*/) +{ + switch (type) + { + case LayerType::Addition: + { + return ConvertAdditionToTosaOperator(inputs, outputs); + } + default: + { + // empty basic block when no tosa mapping implemented/exists + TosaSerializationOperator* op = new TosaSerializationOperator(Op_UNKNOWN, Attribute_NONE, nullptr, {}, {}); + return new TosaSerializationBasicBlock("", {op}, {}, {}, {}); + } + } +} + +// Function called in armnn::OptimizeSubgraphView() when access to armnn::Layer is available +// and there is an option to set tosa basic block data from constant layer tenors available from the input layer. +TosaSerializationBasicBlock* GetTosaMappingFromLayer(Layer* layer) +{ + std::vector inputs; + for (auto inputSlot : layer->GetInputSlots()) + { + inputs.push_back(&inputSlot.GetConnection()->GetTensorInfo()); + } + + std::vector outputs; + for (auto& outputSlot : layer->GetOutputSlots()) + { + outputs.push_back(&outputSlot.GetTensorInfo()); + } + + TosaSerializationBasicBlock* basicBlock = GetTosaMapping(layer->GetType(), + inputs, + outputs, + layer->GetParameters()); + SetBasicBlockConstantTensorData(layer, basicBlock); + return basicBlock; +} diff --git a/src/backends/tosaCommon/common.cmake b/src/backends/tosaCommon/common.cmake new file mode 100644 index 0000000000..f15c063700 --- /dev/null +++ b/src/backends/tosaCommon/common.cmake @@ -0,0 +1,11 @@ +# +# Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT +# + +if(ARMNNTOSAREF) + add_subdirectory(${PROJECT_SOURCE_DIR}/src/backends/tosaCommon) + list(APPEND armnnLibraries armnnTosaBackend) + list(APPEND armnnLibraries armnnTosaBackendOperators) + list(APPEND armnnUnitTestLibraries armnnTosaBackendUnitTests) +endif() diff --git a/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp new file mode 100644 index 0000000000..98c01e2cb8 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/AdditionOperator.hpp @@ -0,0 +1,52 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include + +#include +#include "TosaOperatorUtils.hpp" + +using namespace armnn; +using namespace tosa; + +TosaSerializationBasicBlock* ConvertAdditionToTosaOperator(const std::vector& inputs, + const std::vector& outputs) +{ + // A helper function with static global variables ensures uniqueness + // for dynamically generating input, output and block names + std::string input0Name = std::string("Op_ADD_input0_") + GetUniqueTosaMappingID(); + std::string input1Name = std::string("Op_ADD_input1_") + GetUniqueTosaMappingID(); + std::string outputName = std::string("Op_ADD_output0_") + GetUniqueTosaMappingID(); + std::string blockName = std::string("Op_ADD_block_") + GetUniqueTosaMappingID(); + + TosaSerializationOperator* op = new TosaSerializationOperator(Op_ADD, + Attribute_NONE, + nullptr, + {input0Name, input1Name}, + {outputName}); + + std::vector inputShape0 = GetTosaTensorShape(inputs[0]->GetShape()); + DType inputDType0 = ArmNNToDType(inputs[0]->GetDataType()); + + std::vector inputShape1 = GetTosaTensorShape(inputs[1]->GetShape()); + DType inputDType1 = ArmNNToDType(inputs[1]->GetDataType()); + + std::vector outputShape0 = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType()); + + TosaSerializationTensor* inputTensor0 = new TosaSerializationTensor(input0Name, inputShape0, inputDType0, {}); + TosaSerializationTensor* inputTensor1 = new TosaSerializationTensor(input1Name, inputShape1, inputDType1, {}); + TosaSerializationTensor* outputTensor0 = new TosaSerializationTensor(outputName, outputShape0, outputDType0, {}); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + {op}, // operators + {inputTensor0, inputTensor1, outputTensor0}, // tensors + {input0Name, input1Name}, // inputs + {outputName}); // outputs +} diff --git a/src/backends/tosaCommon/operatorMappings/CMakeLists.txt b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt new file mode 100644 index 0000000000..3965a6ab04 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT +# + +list(APPEND armnnTosaBackendOperators_sources + AdditionOperator.hpp + TosaOperatorUtils.hpp + ) + +add_library(armnnTosaBackendOperators OBJECT ${armnnTosaBackendOperators_sources}) +target_include_directories(armnnTosaBackendOperators PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn) +target_include_directories(armnnTosaBackendOperators PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils) +target_include_directories(armnnTosaBackendOperators PRIVATE ${PROJECT_SOURCE_DIR}/src/backends) diff --git a/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp new file mode 100644 index 0000000000..b887721648 --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/TosaOperatorUtils.hpp @@ -0,0 +1,60 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include + +#include + +using namespace armnn; +using namespace tosa; + +// Function to return Tosa datatype from input ArmNN datatype. +DType ArmNNToDType(const DataType& type) +{ + switch (type) + { + case DataType::Float16: + case DataType::Float32: + case DataType::BFloat16: + return DType_FLOAT; + case DataType::QAsymmU8: + return DType_UINT8; + case DataType::QSymmS8: + case DataType::QAsymmS8: + return DType_INT8; + case DataType::QSymmS16: + return DType_INT16; + case DataType::Signed32: + return DType_INT32; + case DataType::Signed64: + // No signed 64, only DType_INT48. + return DType_UNKNOWN; + case DataType::Boolean: + return DType_BOOL; + default: + return DType_UNKNOWN; + } +} + +// Function to return Tosa tensor shape from input ArmNN tensor shape. +std::vector GetTosaTensorShape(const TensorShape& shape) +{ + std::vector returnShape; + for (u_int32_t i = 0; i < shape.GetNumDimensions(); i++) + { + returnShape.push_back(static_cast(shape[i])); + } + return returnShape; +} + +// Function to return unique int as a string to ensure uniqueness between all input, output and block names. +static int uniqueTosaMappingID = 0; +std::string GetUniqueTosaMappingID() +{ + return std::to_string(++uniqueTosaMappingID); +} diff --git a/src/backends/tosaCommon/test/CMakeLists.txt b/src/backends/tosaCommon/test/CMakeLists.txt new file mode 100644 index 0000000000..56cc66e51c --- /dev/null +++ b/src/backends/tosaCommon/test/CMakeLists.txt @@ -0,0 +1,14 @@ +# +# Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +# SPDX-License-Identifier: MIT +# + +list(APPEND armnnTosaBackendUnitTests_sources + TosaOperatorMappingTests.cpp + ) + +add_library(armnnTosaBackendUnitTests OBJECT ${armnnTosaBackendUnitTests_sources}) +target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/armnn) +target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnUtils) +target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/armnnTestUtils) +target_include_directories(armnnTosaBackendUnitTests PRIVATE ${PROJECT_SOURCE_DIR}/src/backends) diff --git a/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp new file mode 100644 index 0000000000..2f5a56d88d --- /dev/null +++ b/src/backends/tosaCommon/test/TosaOperatorMappingTests.cpp @@ -0,0 +1,124 @@ +// +// Copyright © 2022 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include + +#include + +#include + +using namespace armnn; +using namespace tosa; + +void AssertTosaOneToOneMappingBasicBlock(TosaSerializationBasicBlock* basicBlock, + std::vector shape, + uint32_t numInputs, + uint32_t numOutputs, + Op tosaOp, + std::string operatorString, + DType dataType = DType_FLOAT) +{ + std::string blockStr = operatorString + "_block_"; + ARMNN_ASSERT(basicBlock->GetName().find(blockStr) != std::string::npos); + ARMNN_ASSERT(basicBlock->GetInputs().size() == numInputs); + ARMNN_ASSERT(basicBlock->GetOutputs().size() == numOutputs); + ARMNN_ASSERT(basicBlock->GetOperators().size() == 1); + ARMNN_ASSERT(basicBlock->GetTensors().size() == (numInputs + numOutputs)); + + TosaSerializationOperator* op = basicBlock->GetOperators().at(0); + ARMNN_ASSERT(op->GetInputTensorNames().size() == numInputs); + ARMNN_ASSERT(op->GetOutputTensorNames().size() == numOutputs); + + for (uint32_t i = 0; i < numInputs; i++) + { + std::basic_string blockInputName = basicBlock->GetInputs()[i]; + std::basic_string operatorInputName = op->GetInputTensorNames()[i]; + std::basic_string tensorName = basicBlock->GetTensors()[i]->GetName(); + + std::string opStr = operatorString + "_input" + std::to_string(i) + "_"; + + ARMNN_ASSERT(blockInputName == operatorInputName); + ARMNN_ASSERT(tensorName == operatorInputName); + ARMNN_ASSERT(blockInputName.find(opStr) != std::string::npos); + } + + for (uint32_t i = 0; i < numOutputs; i++) + { + std::basic_string blockOutputName = basicBlock->GetOutputs()[i]; + std::basic_string operatorOutputName = op->GetOutputTensorNames()[i]; + std::basic_string tensorName = basicBlock->GetTensors()[numInputs + i]->GetName(); + + std::string opStr = operatorString + "_output" + std::to_string(i) + "_"; + + ARMNN_ASSERT(blockOutputName == operatorOutputName); + ARMNN_ASSERT(tensorName == operatorOutputName); + ARMNN_ASSERT(blockOutputName.find(opStr) != std::string::npos); + } + + ARMNN_ASSERT(op->GetAttributeType() == Attribute_NONE); + ARMNN_ASSERT(op->GetOp() == tosaOp); + + TosaSerializationTensor* tensor0 = basicBlock->GetTensors()[0]; + ARMNN_ASSERT(tensor0->GetDtype() == dataType); + ARMNN_ASSERT(tensor0->GetData().size() == 0); + ARMNN_ASSERT(tensor0->GetShape() == shape); +} + +TEST_SUITE("TosaOperatorMappingOneToOneTests") +{ +TEST_CASE("GetTosaMapping_AdditionLayer") +{ + TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true); + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::Addition, {&info, &info}, {&info}, BaseDescriptor()); + AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD"); +} + +TEST_CASE("GetTosaMappingFromLayer_AdditionLayer") +{ + IRuntime::CreationOptions options; + IRuntimePtr runtime(IRuntime::Create(options)); + + // Builds up the structure of the network. + INetworkPtr net(INetwork::Create()); + + IConnectableLayer* input0 = net->AddInputLayer(0, "input0"); + IConnectableLayer* input1 = net->AddInputLayer(1, "input1"); + IConnectableLayer* add = net->AddAdditionLayer("add"); + IConnectableLayer* output = net->AddOutputLayer(0, "output"); + + input0->GetOutputSlot(0).Connect(add->GetInputSlot(0)); + input1->GetOutputSlot(0).Connect(add->GetInputSlot(1)); + add->GetOutputSlot(0).Connect(output->GetInputSlot(0)); + + TensorInfo info = TensorInfo({ 1, 2, 4, 2 }, DataType::Float32, 0.0f, 0, true); + + input0->GetOutputSlot(0).SetTensorInfo(info); + input1->GetOutputSlot(0).SetTensorInfo(info); + add->GetOutputSlot(0).SetTensorInfo(info); + + TosaSerializationBasicBlock* basicBlock = + GetTosaMappingFromLayer(PolymorphicDowncast(add)); + AssertTosaOneToOneMappingBasicBlock(basicBlock, { 1, 2, 4, 2 }, 2, 1, Op::Op_ADD, "Op_ADD"); +} + +TEST_CASE("GetTosaMapping_Unimplemented") +{ + TosaSerializationBasicBlock* basicBlock = + GetTosaMapping(LayerType::UnidirectionalSequenceLstm, {}, {}, BaseDescriptor()); + + ARMNN_ASSERT(basicBlock->GetName() == ""); + ARMNN_ASSERT(basicBlock->GetTensors().size() == 0); + ARMNN_ASSERT(basicBlock->GetOperators().size() == 1); + ARMNN_ASSERT(basicBlock->GetInputs().size() == 0); + ARMNN_ASSERT(basicBlock->GetOutputs().size() == 0); + + TosaSerializationOperator* op = basicBlock->GetOperators()[0]; + ARMNN_ASSERT(op->GetAttributeType() == Attribute_NONE); + ARMNN_ASSERT(op->GetOp() == tosa::Op_UNKNOWN); + ARMNN_ASSERT(op->GetInputTensorNames().size() == 0); + ARMNN_ASSERT(op->GetOutputTensorNames().size() == 0); +} +} -- cgit v1.2.1