diff options
Diffstat (limited to 'src/backends/tosaCommon/operatorMappings/ReluOperator.cpp')
-rw-r--r-- | src/backends/tosaCommon/operatorMappings/ReluOperator.cpp | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp b/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp new file mode 100644 index 0000000000..bd1a59670e --- /dev/null +++ b/src/backends/tosaCommon/operatorMappings/ReluOperator.cpp @@ -0,0 +1,121 @@ +// +// Copyright © 2024 Arm Ltd and Contributors. All rights reserved. +// SPDX-License-Identifier: MIT +// +// +// Copyright © 2020 The TensorFlow Authors. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +// + +#include "LeakyReluOperator.hpp" +#include "TosaRescaleOperatorUtils.hpp" + +#include <layers/ActivationLayer.hpp> + +// This function is paraphrased from: +// tensorflow/compiler/mlir/tosa/transforms/legalize_tfl.cc from function ConvertTFLReluOp +TosaSerializationBasicBlock* ConvertReluToTosaOperator(const Layer* layer, + const std::vector<const TensorInfo*>& inputs, + const std::vector<const TensorInfo*>& outputs, + const ActivationDescriptor*) +{ + if (inputs.size() != 1) + { + throw armnn::Exception("ConvertReluToTosaOperator: 1 input tensors required."); + } + + if (outputs.size() != 1) + { + throw armnn::Exception("ConvertReluToTosaOperator: 1 output tensor required."); + } + + std::string inputName = std::string("input_"); + std::string outputName = std::string("output0_"); + std::string blockName = std::string("Op_RELU_block_") + GetUniqueTosaMappingID(); + + // If a layer is present then the block will be used for execution, so input and output names need to be determined + // using the previous and following layers so the graph is connected correctly. For validation this doesn't matter. + if (layer != nullptr) + { + inputName = GenerateUniqueInputName(layer->GetInputSlot(0)); + outputName = GenerateUniqueOutputName(*layer); + } + + std::vector<TosaSerializationTensor*> tensors; + std::vector<TosaSerializationOperator*> operators; + + // Only add input tensors if connected layer is an input layer. + // As intermediate or constant tensors will be created separately. + // There also can't be duplicate tensor. + std::vector<int32_t> inputShape0; + DType inputDType0 = DType::DType_UNKNOWN; + if(inputName.find("input_") != std::string::npos) + { + inputShape0 = GetTosaTensorShape(inputs[0]->GetShape()); + inputDType0 = ArmNNToDType(inputs[0]->GetDataType()); + tensors.push_back(new TosaSerializationTensor(inputName, inputShape0, inputDType0, {})); + } + + std::vector<int32_t> outputShape0 = GetTosaTensorShape(outputs[0]->GetShape()); + DType outputDType0 = ArmNNToDType(outputs[0]->GetDataType()); + tensors.push_back(new TosaSerializationTensor(outputName, outputShape0, outputDType0, {})); + + int32_t clamp_min = 0; + int32_t clamp_max = std::numeric_limits<int32_t>::max(); + std::string clampInputNameStr = inputName; + if (inputDType0 == tosa::DType::DType_INT8 || inputDType0 == tosa::DType::DType_INT16) + { + std::string outputNameRescale = std::string("intermediate0_") + GetUniqueTosaMappingID(); + clampInputNameStr = outputNameRescale; + + double scale = inputs[0]->GetQuantizationScale() / outputs[0]->GetQuantizationScale(); + int32_t input_zp = inputs[0]->GetQuantizationOffset(); + int32_t output_zp = outputs[0]->GetQuantizationOffset(); + + clamp_min = outputs[0]->GetQuantizationOffset(); + if (inputDType0 == tosa::DType::DType_INT8) + { + clamp_min = + clamp_min < std::numeric_limits<int8_t>::min() ? std::numeric_limits<int8_t>::min() : clamp_min; + clamp_max = std::numeric_limits<int8_t>::max(); + } + else + { + clamp_min = + clamp_min < std::numeric_limits<int16_t>::min() ? std::numeric_limits<int16_t>::min() : clamp_min; + clamp_max = std::numeric_limits<int16_t>::max(); + } + + TosaSerializationOperator* rescaleOp = nullptr; + CreateRescaleTosaOperator(inputName, + outputNameRescale, + scale, + input_zp, + output_zp, + false, + true, + &rescaleOp); + operators.push_back(rescaleOp); + tensors.push_back(new TosaSerializationTensor(outputNameRescale, + inputShape0, + inputDType0, + {})); + } + + TosaClampAttribute attribute(clamp_min, clamp_max, 0, std::numeric_limits<float>::max()); + auto* clamp_op = new TosaSerializationOperator(Op_CLAMP, + Attribute_ClampAttribute, + &attribute, + {clampInputNameStr}, + {outputName}); + operators.push_back(clamp_op); + + // operatorInputNames/operatorOutputNames ends up being the same as + // blockInputNames/blockOutputNames for one-to-one ArmNN to Tosa mappings + return new TosaSerializationBasicBlock(blockName, // name + mainName, // region name + operators, // operators + tensors, // tensors + {inputName}, // inputs + {outputName}); // outputs +} |