blob: e2e5f7618da7b9ae397d2c84d16a6894c5810f16 [file] [log] [blame]
Francis Murtaghc4fb0dd2023-03-16 17:01:56 +00001//
2// Copyright © 2023 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
Francis Murtagh36d94ef2023-04-28 14:05:43 +01005#pragma once
6
7#include <OpaqueDelegateUtils.hpp>
8
9namespace armnnOpaqueDelegate
10{
11
12TfLiteStatus VisitDequantizeOperator(DelegateData& delegateData,
13 TfLiteOpaqueContext* tfLiteContext,
14 TfLiteOpaqueNode* tfLiteNode,
15 int nodeIndex,
16 int32_t operatorCode)
17{
18 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
19 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
20
21 // Gather input indices and use to get input tensor.
22 const int* inputTensors;
23 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
24 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
25 {
26 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
27 tfLiteContext,
28 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
29 nodeIndex);
30 return kTfLiteError;
31 }
32
33 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
Mike Kelly080d45d2023-11-10 17:11:53 +000034
Francis Murtagh36d94ef2023-04-28 14:05:43 +010035 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
36 {
37 return kTfLiteError;
38 }
39
40 // Gather output indices and use to get output tensors.
41 int numOutputs = 0;
42 const int* outputTensors;
43 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
44 {
45 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
46 tfLiteContext,
47 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
48 nodeIndex);
49 return kTfLiteError;
50 }
51
52 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
53 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
54 {
55 return kTfLiteError;
56 }
57
58 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
59 armnn::TensorInfo outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
60
61 UpdateConstantTensorOutputs(inputTensorInfo, outputTensorInfo);
62
63 bool isSupported = false;
64 armnn::BackendId setBackend;
65 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
66 {
Mike Kelly080d45d2023-11-10 17:11:53 +000067 // If this is a Dequantize with a Constant input then will be replaced by a Constant layer that contains the
68 // dequantized values during optimization so there's no need to check if it can be supported by the backend
69 if (IsConstantTensor(tfLiteInputTensor))
70 {
71 isSupported = true;
72 }
73 else
74 {
75 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("DEQUANTIZE",
76 tfLiteContext,
77 IsDequantizeSupported,
78 delegateData.m_Backends,
79 isSupported,
80 setBackend,
81 inputTensorInfo,
82 outputTensorInfo);
83 }
Francis Murtagh36d94ef2023-04-28 14:05:43 +010084 };
85
86 if (!delegateData.m_Network)
87 {
88 validateFunc(outputTensorInfo, isSupported);
89 return isSupported ? kTfLiteOk : kTfLiteError;
90 }
91
Mike Kellya2806502023-08-03 10:42:11 +010092 auto layerName = GetName(armnn::LayerType::Dequantize, nodeIndex);
93 armnn::IConnectableLayer* dequantizeLayer = delegateData.m_Network->AddDequantizeLayer(layerName.c_str());
Francis Murtagh36d94ef2023-04-28 14:05:43 +010094 dequantizeLayer->SetBackendId(setBackend);
95 ARMNN_ASSERT(dequantizeLayer != nullptr);
96
97 armnn::IOutputSlot& outputSlot = dequantizeLayer->GetOutputSlot(0);
98 outputSlot.SetTensorInfo(outputTensorInfo);
99
100 auto inputsTensorsProcess = ProcessInputs(dequantizeLayer,
101 delegateData,
102 tfLiteContext,
Mike Kellya2806502023-08-03 10:42:11 +0100103 tfLiteNode,
104 nodeIndex);
Francis Murtagh36d94ef2023-04-28 14:05:43 +0100105 if (inputsTensorsProcess == kTfLiteError)
106 {
107 return inputsTensorsProcess;
108 }
109
110 return Connect(dequantizeLayer, tfLiteContext, tfLiteNode, delegateData);
111}
112
113TfLiteStatus VisitQuantizeOperator(DelegateData& delegateData,
114 TfLiteOpaqueContext* tfLiteContext,
115 TfLiteOpaqueNode* tfLiteNode,
116 int nodeIndex,
117 int32_t operatorCode)
118{
119 TF_LITE_ENSURE_STATUS(ValidateNumInputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
120 TF_LITE_ENSURE_STATUS(ValidateNumOutputs(tfLiteContext, tfLiteNode, 1, nodeIndex));
121
122 // Gather input indices and use to get input tensor.
123 const int* inputTensors;
124 auto numInputs = TfLiteOpaqueNodeNumberOfInputs(tfLiteNode);
125 if (TfLiteOpaqueNodeInputs(tfLiteNode, &inputTensors, &numInputs) != kTfLiteOk)
126 {
127 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
128 tfLiteContext,
129 "TfLiteArmnnOpaqueDelegate: Unable to gather input tensor indices from node #%d: ",
130 nodeIndex);
131 return kTfLiteError;
132 }
133
134 const TfLiteOpaqueTensor* tfLiteInputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, inputTensors[0]);
135 if (!IsValid(tfLiteContext, tfLiteInputTensor, operatorCode, nodeIndex))
136 {
137 return kTfLiteError;
138 }
139
140 // Gather output indices and use to get output tensors.
141 int numOutputs = 0;
142 const int* outputTensors;
143 if (TfLiteOpaqueNodeOutputs(tfLiteNode, &outputTensors, &numOutputs) != kTfLiteOk)
144 {
145 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
146 tfLiteContext,
147 "TfLiteArmnnOpaqueDelegate: Unable to gather output tensor indices from node #%d: ",
148 nodeIndex);
149 return kTfLiteError;
150 }
151
152 const TfLiteOpaqueTensor* tfLiteOutputTensor = TfLiteOpaqueContextGetOpaqueTensor(tfLiteContext, outputTensors[0]);
153 if (!IsValid(tfLiteContext, tfLiteOutputTensor, operatorCode, nodeIndex))
154 {
155 return kTfLiteError;
156 }
157
158 // Only affine per-layer quantization is supported.
159 if (!IsAffineQuantization(*tfLiteOutputTensor))
160 {
161 TF_LITE_OPAQUE_MAYBE_KERNEL_LOG(
162 tfLiteContext,
163 "TfLiteArmnnOpaqueDelegate: Only affine per-layer quantization is supported in operator #%d node #%d: ",
164 operatorCode, nodeIndex);
165 return kTfLiteError;
166 }
167
168 const armnn::TensorInfo& inputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteInputTensor);
169 const armnn::TensorInfo& outputTensorInfo = GetTensorInfoForTfLiteOpaqueTensor(tfLiteOutputTensor, true);
170
171 bool isSupported = false;
172 armnn::BackendId setBackend;
173 auto validateFunc = [&](const armnn::TensorInfo& outputTensorInfo, bool& isSupported)
174 {
175 FORWARD_LAYER_OPAQUE_SUPPORT_FUNC("QUANTIZE",
176 tfLiteContext,
177 IsQuantizeSupported,
178 delegateData.m_Backends,
179 isSupported,
180 setBackend,
181 inputTensorInfo,
182 outputTensorInfo);
183 };
184
185 if (!delegateData.m_Network)
186 {
187 validateFunc(outputTensorInfo, isSupported);
188 return isSupported ? kTfLiteOk : kTfLiteError;
189 }
190
Mike Kellya2806502023-08-03 10:42:11 +0100191 auto layerName = GetName(armnn::LayerType::Quantize, nodeIndex);
192 armnn::IConnectableLayer* quantizeLayer = delegateData.m_Network->AddQuantizeLayer(layerName.c_str());
Francis Murtagh36d94ef2023-04-28 14:05:43 +0100193 quantizeLayer->SetBackendId(setBackend);
194 ARMNN_ASSERT(quantizeLayer != nullptr);
195
196 armnn::IOutputSlot& outputSlot = quantizeLayer->GetOutputSlot(0);
197 outputSlot.SetTensorInfo(outputTensorInfo);
198
199 // try to connect the Constant Inputs if there are any
Mike Kellya2806502023-08-03 10:42:11 +0100200 if (ProcessInputs(quantizeLayer, delegateData, tfLiteContext, tfLiteNode, nodeIndex) != kTfLiteOk)
Francis Murtagh36d94ef2023-04-28 14:05:43 +0100201 {
202 return kTfLiteError;
203 }
204
205 return Connect(quantizeLayer, tfLiteContext, tfLiteNode, delegateData);
206}
207
208} // namespace armnnOpaqueDelegate