• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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