GitHub #667: Neon fold padding into average pool 2D quantization bug fix.

  * Originated from a GitHub issue: https://github.com/ARM-software/armnn/issues/667
  * Initially, Arm NN supports the pool 2D operation because there is no padding
    on the pool2d. Neon failure occurs when padding is followed by average pool 2D
    due to folding optimization.
  * Here we prevent the folding optimization from happening for the above special case
    and add it in as a backend specific optimization.

Signed-off-by: Cathal Corbett <cathal.corbett@arm.com>
Change-Id: Ia0fd90c3a6b4b9d29c81106f154617d2e893e26b
diff --git a/src/backends/backendsCommon/SubgraphUtils.hpp b/src/backends/backendsCommon/SubgraphUtils.hpp
new file mode 100644
index 0000000..bd3d698
--- /dev/null
+++ b/src/backends/backendsCommon/SubgraphUtils.hpp
@@ -0,0 +1,99 @@
+//
+// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
+// SPDX-License-Identifier: MIT
+//
+
+#pragma once
+
+#include <optimizations/FoldPadIntoLayer2d.hpp>
+
+namespace armnn
+{
+
+namespace
+{
+
+//
+// this helper only works if all layers where the inputs connect to are not selected
+//
+
+SubgraphView::IInputSlots CreateIInputsFrom(const std::vector<armnn::IConnectableLayer*>& layers)
+{
+    SubgraphView::IInputSlots result;
+    for (auto&& layer : layers)
+    {
+        for (unsigned int i = 0 ; i < layer->GetNumInputSlots(); ++i)
+        {
+            result.push_back(&(layer->GetInputSlot(i)));
+        }
+    }
+    return result;
+}
+
+//
+// this helper only works if all layers where the outputs connect to are not selected
+//
+
+SubgraphView::IOutputSlots CreateIOutputsFrom(const std::vector<armnn::IConnectableLayer*>& layers)
+{
+    SubgraphView::IOutputSlots result;
+    for (auto &&layer: layers)
+    {
+        for (unsigned int i = 0; i < layer->GetNumOutputSlots(); ++i)
+        {
+            result.push_back(&(layer->GetOutputSlot(i)));
+        }
+    }
+    return result;
+}
+
+}
+
+inline void ReportUntouchedLayers(OptimizationViews& optimizationViews, std::map<LayerGuid, Layer*> untouched)
+{
+    std::vector<Layer*> untouchedVector;
+    for (const auto& pair : untouched)
+    {
+        Layer* layer = pair.second;
+        SubgraphView subgraphView({layer},
+                                  CreateIInputsFrom({layer}),
+                                  CreateIOutputsFrom({layer}));
+        optimizationViews.AddUntouchedSubgraph(std::move(subgraphView));
+    }
+}
+
+template<typename LayerType>
+LayerType* FoldPadLayer(OptimizationViews& optimizationViews,
+                        LayerType* baseLayer,
+                        LayerType* replacementLayer,
+                        PadLayer* padLayer)
+{
+    SubgraphView substitutionSubgraph({padLayer, baseLayer},
+                                      CreateIInputsFrom({padLayer}),
+                                      CreateIOutputsFrom({baseLayer}));
+    SubgraphView replacementSubgraph(replacementLayer);
+
+    optimizationViews.AddSubstitution({substitutionSubgraph, replacementSubgraph});
+
+    return replacementLayer;
+}
+
+template<typename LayerType>
+LayerType* FoldPadIntoAveragePool2d(OptimizationViews& optimizationViews,
+                                    Pooling2dLayer* baseLayer,
+                                    Pooling2dDescriptor& poolDescriptor,
+                                    PadLayer* padLayer)
+{
+        IConnectableLayer* replacement =
+            optimizationViews.GetINetwork()->AddPooling2dLayer(poolDescriptor, "folded-pad-into-pool2d");
+        LayerType* replacementLayer = PolymorphicDowncast<LayerType*>(replacement);
+
+        FoldPadLayer(optimizationViews,
+                     baseLayer,
+                     replacementLayer,
+                     padLayer);
+
+        return replacementLayer;
+}
+
+} // namespace armnn