1 //
2 // Copyright © 2017,2022 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #pragma once
6
7 #include "LayerFwd.hpp"
8
9 #include <armnn/backends/ITensorHandleFactory.hpp>
10 #include <OutputHandler.hpp>
11 #include <backendsCommon/TensorHandleFactoryRegistry.hpp>
12 #include <backendsCommon/WorkloadDataCollector.hpp>
13 #include <backendsCommon/WorkloadInfo.hpp>
14 #include "InternalTypes.hpp"
15 #include "SerializeLayerParameters.hpp"
16 #include "DllExport.hpp"
17
18 #include <armnn/Types.hpp>
19 #include <armnn/Tensor.hpp>
20 #include <armnn/INetwork.hpp>
21 #include <armnn/utility/IgnoreUnused.hpp>
22 #include <armnn/utility/NumericCast.hpp>
23 #include <armnn/utility/PolymorphicDowncast.hpp>
24
25 #include <algorithm>
26 #include <functional>
27 #include <iostream>
28 #include <list>
29 #include <memory>
30 #include <string>
31 #include <vector>
32 #include <armnn/backends/WorkloadData.hpp>
33
34 namespace armnn
35 {
36
37 class IWorkload;
38 class IWorkloadFactory;
39 class Layer;
40 class Graph;
41
42 class InputSlot final : public IInputSlot
43 {
44 public:
InputSlot(Layer & owner,unsigned int slotIndex)45 explicit InputSlot(Layer& owner, unsigned int slotIndex)
46 : m_OwningLayer(owner)
47 , m_Connection(nullptr)
48 , m_SlotIndex(slotIndex)
49 {}
50
51 ~InputSlot();
52
GetOwningLayer() const53 Layer& GetOwningLayer() const { return m_OwningLayer; }
GetSlotIndex() const54 unsigned int GetSlotIndex() const override { return m_SlotIndex; }
55
GetConnectedOutputSlot() const56 const OutputSlot* GetConnectedOutputSlot() const { return m_Connection; }
GetConnectedOutputSlot()57 OutputSlot* GetConnectedOutputSlot() { return m_Connection; }
58
59 const IConnectableLayer& GetOwningIConnectableLayer() const override;
60 IConnectableLayer& GetOwningIConnectableLayer() override;
61
62 /// Links the slot to an output slot or breaks an existing link if passing nullptr.
SetConnection(OutputSlot * source)63 void SetConnection(OutputSlot* source)
64 {
65 if (m_Connection != nullptr && source != nullptr)
66 {
67 throw InvalidArgumentException("Tried to connect an output slot to an input slot, "
68 "but the latter already has a connection");
69 }
70 m_Connection = source;
71 }
72
73 // Inserts single-output existing layer at this point in the graph.
74 void Insert(Layer& layer);
75
76 // InputSlot
77
78 const IOutputSlot* GetConnection() const override;
79 IOutputSlot* GetConnection() override;
80
81 private:
82 Layer& m_OwningLayer;
83 OutputSlot* m_Connection;
84 const unsigned int m_SlotIndex;
85 };
86
87 class OutputSlot final : public IOutputSlot
88 {
89 public:
OutputSlot(Layer & owner,OutputHandler & outputHandler)90 explicit OutputSlot(Layer& owner, OutputHandler& outputHandler)
91 : m_OwningLayer(owner)
92 , m_OutputHandler(outputHandler)
93 , m_TensorHandleFactoryId(ITensorHandleFactory::LegacyFactoryId)
94 {}
95
96 OutputSlot(const OutputSlot&) = delete;
97 OutputSlot& operator=(const OutputSlot&) = delete;
98 OutputSlot& operator=(OutputSlot&&) = delete;
99
100 OutputSlot(OutputSlot&&) = default;
101
~OutputSlot()102 ~OutputSlot()
103 {
104 try
105 {
106 // Coverity fix: DisconnectAll() may throw uncaught exceptions.
107 DisconnectAll();
108 }
109 catch (const std::exception& e)
110 {
111 // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
112 // exception of type std::length_error.
113 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
114 std::cerr << "WARNING: An error has occurred when disconnecting all output slots: "
115 << e.what() << std::endl;
116 }
117 }
118
GetOwningLayer() const119 Layer& GetOwningLayer() const { return m_OwningLayer; }
120
121 const IConnectableLayer& GetOwningIConnectableLayer() const override;
122 IConnectableLayer& GetOwningIConnectableLayer() override;
123
124 LayerGuid GetOwningLayerGuid() const override;
125
GetOutputHandler() const126 const OutputHandler& GetOutputHandler() const { return m_OutputHandler; }
GetOutputHandler()127 OutputHandler& GetOutputHandler() { return m_OutputHandler; }
128
129 int Connect(InputSlot& destination);
130 void Disconnect(InputSlot& slot);
131
GetConnections() const132 const std::vector<InputSlot*>& GetConnections() const { return m_Connections; }
GetEdgeStrategies() const133 const std::vector<EdgeStrategy>& GetEdgeStrategies() const { return m_EdgeStrategies; }
134
135 bool ValidateTensorShape(const TensorShape& shape) const;
136
137 // Disconnect all conections.
138 void DisconnectAll();
139
140 /// Moves all connections to another OutputSlot.
141 void MoveAllConnections(OutputSlot& destination);
142
143 // IOutputSlot
144
GetNumConnections() const145 unsigned int GetNumConnections() const override { return armnn::numeric_cast<unsigned int>(m_Connections.size()); }
146 const InputSlot* GetConnection(unsigned int index) const override;
147 InputSlot* GetConnection(unsigned int index) override;
148
149 void SetTensorInfo(const TensorInfo& tensorInfo) override;
150 const TensorInfo& GetTensorInfo() const override;
151 bool IsTensorInfoSet() const override;
152
Connect(IInputSlot & destination)153 int Connect(IInputSlot& destination) override
154 {
155 return Connect(*PolymorphicDowncast<InputSlot*>(&destination));
156 }
157
Disconnect(IInputSlot & slot)158 void Disconnect(IInputSlot& slot) override
159 {
160 return Disconnect(*PolymorphicDowncast<InputSlot*>(&slot));
161 }
162
163 unsigned int CalculateIndexOnOwner() const override;
164
165 bool operator==(const OutputSlot& other) const;
166
167 void SetTensorHandleFactory(const ITensorHandleFactory::FactoryId& id);
168 ITensorHandleFactory::FactoryId GetTensorHandleFactoryId() const;
169
170 void SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy);
171 EdgeStrategy GetEdgeStrategyForConnection(unsigned int connectionIdx) const;
172
173 private:
174 void ValidateConnectionIndex(unsigned int index) const;
175
176 Layer& m_OwningLayer;
177 OutputHandler& m_OutputHandler;
178 std::vector<InputSlot*> m_Connections;
179
180 ITensorHandleFactory::FactoryId m_TensorHandleFactoryId;
181 std::vector<EdgeStrategy> m_EdgeStrategies;
182 };
183
184 // InputSlot inlines that need OutputSlot declaration.
185
~InputSlot()186 inline InputSlot::~InputSlot()
187 {
188 if (m_Connection != nullptr)
189 {
190 try
191 {
192 // Coverity fix: Disconnect() may throw uncaught exceptions.
193 m_Connection->Disconnect(*this);
194 }
195 catch (const std::exception& e)
196 {
197 // Coverity fix: BOOST_LOG_TRIVIAL (typically used to report errors) may throw an
198 // exception of type std::length_error.
199 // Using stderr instead in this context as there is no point in nesting try-catch blocks here.
200 std::cerr << "WARNING: An error has occurred when disconnecting an input slot: "
201 << e.what() << std::endl;
202 }
203 }
204 }
205
GetConnection() const206 inline const IOutputSlot* InputSlot::GetConnection() const { return GetConnectedOutputSlot(); }
GetConnection()207 inline IOutputSlot* InputSlot::GetConnection() { return GetConnectedOutputSlot(); }
208
209
210 class ScopedTensorHandle;
211
212 // Base layer class
213
214 using LayerPriority = unsigned int;
215 using AdditionalInfoObjectPtr = std::shared_ptr<void>;
216
217 class Layer : public IConnectableLayer
218 {
219 public:
220 /// @param name - Optional name for the layer (may be nullptr).
221 Layer(unsigned int numInputSlots, unsigned int numOutputSlots, LayerType type, const char* name);
222 Layer(unsigned int numInputSlots, unsigned int numOutputSlots, LayerType type, DataLayout layout, const char* name);
223
224 void ExecuteStrategy(IStrategy& strategy) const override;
225
226
GetNameStr() const227 const std::string& GetNameStr() const
228 {
229 return m_LayerName;
230 }
231
GetOutputHandler(unsigned int i=0) const232 const OutputHandler& GetOutputHandler(unsigned int i = 0) const
233 {
234 return m_OutputHandlers[i];
235 }
236
GetOutputHandler(unsigned int i=0)237 OutputHandler& GetOutputHandler(unsigned int i = 0)
238 {
239 return const_cast<OutputHandler&>(const_cast<const Layer*>(this)->GetOutputHandler(i));
240 }
241
GetShapeInferenceMethod() const242 ShapeInferenceMethod GetShapeInferenceMethod() const { return m_ShapeInferenceMethod; };
GetAllowExpandedDims() const243 bool GetAllowExpandedDims() const { return m_AllowExpandedDims; };
244
GetInputSlots() const245 const std::vector<InputSlot>& GetInputSlots() const { return m_InputSlots; }
GetOutputSlots() const246 const std::vector<OutputSlot>& GetOutputSlots() const { return m_OutputSlots; }
247
248 // Allows non-const access to input slots, but don't expose vector (vector size is fixed at layer construction).
BeginInputSlots()249 std::vector<InputSlot>::iterator BeginInputSlots() { return m_InputSlots.begin(); }
EndInputSlots()250 std::vector<InputSlot>::iterator EndInputSlots() { return m_InputSlots.end(); }
251
252 // Allows non-const access to output slots, but don't expose vector (vector size is fixed at layer construction).
BeginOutputSlots()253 std::vector<OutputSlot>::iterator BeginOutputSlots() { return m_OutputSlots.begin(); }
EndOutputSlots()254 std::vector<OutputSlot>::iterator EndOutputSlots() { return m_OutputSlots.end(); }
255
256 // Checks whether the outputs of this layer don't have any connection.
IsOutputUnconnected()257 bool IsOutputUnconnected()
258 {
259 unsigned int numConnections = 0;
260
261 for (auto&& output : GetOutputSlots())
262 {
263 numConnections += output.GetNumConnections();
264 }
265
266 return (GetNumOutputSlots() > 0) && (numConnections == 0);
267 }
268
269 // Used for sorting.
270 void ResetPriority() const;
271 LayerPriority GetPriority() const;
272
GetType() const273 LayerType GetType() const override { return m_Type; }
274
275 DataType GetDataType() const;
276
GetBackendId() const277 const BackendId& GetBackendId() const { return m_BackendId; }
SetBackendId(const BackendId & id)278 void SetBackendId(const BackendId& id) override { m_BackendId = id; }
279
280 // Virtuals
281
282 virtual std::unique_ptr<IWorkload> CreateWorkload(const IWorkloadFactory& factory) const = 0;
283
284 virtual void CreateTensorHandles(const TensorHandleFactoryRegistry& registry,
285 const IWorkloadFactory& factory,
286 const bool IsMemoryManaged = true);
287
288 /// Creates a dynamically-allocated copy of this layer.
289 /// @param graph - The Graph into which this Layer is being cloned.
290 virtual Layer* Clone(Graph& graph) const = 0;
291
292 void VerifyLayerConnections(unsigned int expectedConnections, const CheckLocation& location) const;
293
294 virtual void ValidateTensorShapesFromInputs() = 0;
295
296 std::vector<TensorShape> InferOutputShapes(const std::vector<TensorShape>& inputShapes) const override;
297
298 /// Helper to serialize the layer parameters to string.
299 /// (currently used in DotSerializer and company).
300 virtual void SerializeLayerParameters(ParameterStringifyFunction& fn) const;
301
302 // Free up the constant source data
303 virtual void ReleaseConstantData();
304
305 template<typename Op>
OperateOnConstantTensors(Op op)306 void OperateOnConstantTensors(Op op)
307 {
308 for (auto constant : GetConstantTensorsByRef())
309 {
310 if (constant.get())
311 {
312 op(constant);
313 }
314 }
315 };
316
317 // IConnectableLayer
318
GetName() const319 const char* GetName() const override { return m_LayerName.c_str(); }
320
GetNumInputSlots() const321 unsigned int GetNumInputSlots() const override { return static_cast<unsigned int>(m_InputSlots.size()); }
GetNumOutputSlots() const322 unsigned int GetNumOutputSlots() const override { return static_cast<unsigned int>(m_OutputSlots.size()); }
323
GetInputSlot(unsigned int index) const324 const InputSlot& GetInputSlot(unsigned int index) const override { return m_InputSlots.at(index); }
GetInputSlot(unsigned int index)325 InputSlot& GetInputSlot(unsigned int index) override { return m_InputSlots.at(index); }
GetOutputSlot(unsigned int index=0) const326 const OutputSlot& GetOutputSlot(unsigned int index = 0) const override { return m_OutputSlots.at(index); }
GetOutputSlot(unsigned int index=0)327 OutputSlot& GetOutputSlot(unsigned int index = 0) override { return m_OutputSlots.at(index); }
328
SetGuid(LayerGuid guid)329 void SetGuid(LayerGuid guid) { m_Guid = guid; }
GetGuid() const330 LayerGuid GetGuid() const final { return m_Guid; }
331
AddRelatedLayerName(const std::string layerName)332 void AddRelatedLayerName(const std::string layerName) { m_RelatedLayerNames.emplace_back(layerName); }
333
GetRelatedLayerNames()334 const std::list<std::string>& GetRelatedLayerNames() { return m_RelatedLayerNames; }
335
336 virtual void Reparent(Graph& dest, std::list<Layer*>::const_iterator iterator) = 0;
337
BackendSelectionHint(Optional<BackendId> backend)338 void BackendSelectionHint(Optional<BackendId> backend) final
339 {
340 m_BackendHint = backend;
341 }
GetBackendHint() const342 Optional<BackendId> GetBackendHint() const { return m_BackendHint; }
343
SetShapeInferenceMethod(ShapeInferenceMethod shapeInferenceMethod)344 void SetShapeInferenceMethod(ShapeInferenceMethod shapeInferenceMethod)
345 {
346 m_ShapeInferenceMethod = shapeInferenceMethod;
347 }
348
SetAllowExpandedDims(bool allowExpandedDims)349 void SetAllowExpandedDims(bool allowExpandedDims)
350 {
351 m_AllowExpandedDims = allowExpandedDims;
352 }
353
354 template<typename T>
GetAdditionalInformation() const355 std::shared_ptr<T> GetAdditionalInformation() const
356 {
357 return std::static_pointer_cast<T>(m_AdditionalInfoObject);
358 }
359
SetAdditionalInfoForObject(const AdditionalInfoObjectPtr & additionalInfo)360 void SetAdditionalInfoForObject(const AdditionalInfoObjectPtr& additionalInfo)
361 {
362 m_AdditionalInfoObject = additionalInfo;
363 }
364
GetParameters() const365 virtual const BaseDescriptor& GetParameters() const override { return m_NullDescriptor; }
366
367 protected:
368 // Graph needs access to the virtual destructor.
369 friend class Graph;
370 virtual ~Layer() = default;
371
372 template <typename QueueDescriptor>
CollectQueueDescriptorInputs(QueueDescriptor & descriptor,WorkloadInfo & info) const373 void CollectQueueDescriptorInputs(QueueDescriptor& descriptor, WorkloadInfo& info) const
374 {
375 WorkloadDataCollector dataCollector(descriptor.m_Inputs, info.m_InputTensorInfos);
376 CollectWorkloadInputs(dataCollector);
377 }
378
379 template <typename QueueDescriptor>
CollectQueueDescriptorOutputs(QueueDescriptor & descriptor,WorkloadInfo & info) const380 void CollectQueueDescriptorOutputs(QueueDescriptor& descriptor, WorkloadInfo& info) const
381 {
382 WorkloadDataCollector dataCollector(descriptor.m_Outputs, info.m_OutputTensorInfos);
383 CollectWorkloadOutputs(dataCollector);
384 }
385
386 void ValidateAndCopyShape(const TensorShape& outputShape,
387 const TensorShape& inferredShape,
388 const ShapeInferenceMethod shapeInferenceMethod,
389 const std::string& layerName,
390 const unsigned int outputSlotIndex = 0);
391
392 void VerifyShapeInferenceType(const TensorShape& outputShape, ShapeInferenceMethod shapeInferenceMethod);
393
394 /// Helper function to reduce duplication in *Layer::CreateWorkload.
395 template <typename QueueDescriptor>
PrepInfoAndDesc(QueueDescriptor & descriptor) const396 WorkloadInfo PrepInfoAndDesc(QueueDescriptor& descriptor) const
397 {
398 WorkloadInfo info;
399 CollectQueueDescriptorInputs(descriptor, info);
400 CollectQueueDescriptorOutputs(descriptor, info);
401 return info;
402 }
403
404 template <typename LayerType, typename ... Params>
405 LayerType* CloneBase(Graph& graph, Params&& ... params) const;
406
407 // Retrieve the Handles to the constants
408 // Marking this as override and having this here keeps IConnectable abstract with only pure virtual function
409 virtual ConstantTensors GetConstantTensorsByRef() override final;
410
411 // Retrieve the Handles to the constants
412 // Marking this as override and having this here keeps IConnectable abstract with only pure virtual function
GetConstantTensorsByRef() const413 virtual ImmutableConstantTensors GetConstantTensorsByRef() const override { return ImmutableConstantTensors(); };
414
415 // "Blob"
416 AdditionalInfoObjectPtr m_AdditionalInfoObject;
417
418 // Utility method to set a pointer in the queueDescriptor to the "blob" location in the layer
419 void SetAdditionalInfo(QueueDescriptor& descriptor) const;
420
421 private:
422 void CollectWorkloadInputs(WorkloadDataCollector& dataCollector) const;
423 void CollectWorkloadOutputs(WorkloadDataCollector& dataCollector) const;
424
425 protected:
426 std::vector<OutputHandler> m_OutputHandlers;
427 ShapeInferenceMethod m_ShapeInferenceMethod;
428
429 private:
430 const std::string m_LayerName;
431
432 std::vector<InputSlot> m_InputSlots;
433 std::vector<OutputSlot> m_OutputSlots;
434
435 const LayerType m_Type;
436 BackendId m_BackendId;
437 Optional<BackendId> m_BackendHint;
438
439 /// Used for sorting.
440 mutable LayerPriority m_Priority = 0;
441 mutable bool m_Visiting = false;
442
443 bool m_AllowExpandedDims = false;
444
445 LayerGuid m_Guid;
446
447 std::list<std::string> m_RelatedLayerNames;
448
449 /// returned by layers which have no parameters associated with them.
450 /// has to be a member as it is returned as a const reference
451 /// declared static so that there is only ever one of them in memory
452 ARMNN_DLLEXPORT static NullDescriptor m_NullDescriptor;
453 };
454
455 // A layer user-provided data can be bound to (e.g. inputs, outputs).
456 class BindableLayer : public Layer
457 {
458 public:
BindableLayer(unsigned int numInputSlots,unsigned int numOutputSlots,LayerType type,const char * name,LayerBindingId id)459 BindableLayer(unsigned int numInputSlots,
460 unsigned int numOutputSlots,
461 LayerType type,
462 const char* name,
463 LayerBindingId id)
464 : Layer(numInputSlots, numOutputSlots, type, name)
465 , m_Id(id)
466 {
467 }
468
GetBindingId() const469 LayerBindingId GetBindingId() const { return m_Id; };
470
ExecuteStrategy(IStrategy & strategy) const471 void ExecuteStrategy(IStrategy& strategy) const override
472 {
473 strategy.ExecuteStrategy(this, BaseDescriptor(), {}, GetName(), GetBindingId());
474 }
475
476 protected:
477 ~BindableLayer() = default;
478
479 private:
480 LayerBindingId m_Id;
481 };
482
483 } //namespace armnn
484