summaryrefslogtreecommitdiff
path: root/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/enco/core/src/Transforms/AvgPoolLowering.cpp')
-rw-r--r--compiler/enco/core/src/Transforms/AvgPoolLowering.cpp229
1 files changed, 229 insertions, 0 deletions
diff --git a/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp b/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp
new file mode 100644
index 000000000..17502fb1f
--- /dev/null
+++ b/compiler/enco/core/src/Transforms/AvgPoolLowering.cpp
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 2018 Samsung Electronics Co., Ltd. All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AvgPoolLowering.h"
+#include "IRUtils.h"
+
+#include <coco/IR/FeatureLayouts.h>
+
+#include <nncc/core/ADT/feature/Shape.h>
+#include <nncc/core/ADT/feature/HWCLayout.h>
+
+#include <set>
+#include <cassert>
+
+using namespace nncc::core::ADT;
+using nncc::core::ADT::feature::num_elements;
+
+namespace
+{
+
+bool empty(coco::Padding2D *pad)
+{
+ return (pad->top() == 0) && (pad->bottom() == 0) && (pad->left() == 0) && (pad->right() == 0);
+}
+
+/**
+ * @brief Return a set of AvgPool2D operations (in Eval instruction) that SHOULD be lowered
+ */
+std::set<coco::AvgPool2D *> candidates(coco::Module *m)
+{
+ std::set<coco::AvgPool2D *> res;
+
+ for (auto I : enco::instr_sequence(m))
+ {
+ if (auto eval = I->asEval())
+ {
+ if (auto avgpool = eval->op()->asAvgPool2D())
+ {
+ /* Originally it was preferred to use `auto load = avgpool->arg()->asLoad()' for
+ * consitent style with other if statements.
+ * Someone may think compiler will be happy because `load` in `if` statement can
+ * be considered as a use, however, it turend out that it is not the case.
+ */
+ if (avgpool->arg()->asLoad())
+ {
+ if (avgpool->divisor() == coco::AvgPool2D::Divisor::Static)
+ {
+ res.insert(avgpool);
+ }
+ }
+ }
+ }
+ }
+
+ return res;
+}
+
+} // namespace
+
+namespace
+{
+namespace ShapeTransform
+{
+
+class Pad
+{
+public:
+ Pad(const coco::Padding2D *pad) : _pad{pad}
+ {
+ // DO NOTHING
+ }
+
+public:
+ /// @brief Return the expected OFM shape for a given IFM shape
+ feature::Shape forward(const feature::Shape &ifm_shape) const
+ {
+ const uint32_t OFM_C = ifm_shape.depth();
+ const uint32_t OFM_H = ifm_shape.height() + _pad->top() + _pad->bottom();
+ const uint32_t OFM_W = ifm_shape.width() + _pad->left() + _pad->right();
+
+ return feature::Shape{OFM_C, OFM_H, OFM_W};
+ }
+
+private:
+ const coco::Padding2D *_pad;
+};
+
+} // namespace ShapeTransform
+
+ShapeTransform::Pad shape_xform(const coco::Padding2D *pad) { return ShapeTransform::Pad{pad}; }
+
+} // namespace
+
+namespace
+{
+
+class PadInstrBuilder final
+{
+public:
+ PadInstrBuilder(const coco::Padding2D *pad) : _pad{pad}
+ {
+ // DO NOTHING
+ }
+
+public:
+ coco::Instr *build(coco::FeatureObject *ifm_obj, coco::FeatureObject *ofm_obj) const
+ {
+ assert(ifm_obj->module() == ofm_obj->module());
+ auto m = ifm_obj->module();
+ assert(m != nullptr);
+
+ auto load_op = m->entity()->op()->create<coco::Load>();
+
+ load_op->object(ifm_obj);
+
+ auto pad_op = m->entity()->op()->create<coco::PadF>();
+
+ pad_op->arg(load_op);
+
+ pad_op->pad()->top(_pad->top());
+ pad_op->pad()->bottom(_pad->bottom());
+ pad_op->pad()->left(_pad->left());
+ pad_op->pad()->right(_pad->right());
+
+ auto pad_instr = m->entity()->instr()->create<coco::Eval>();
+
+ pad_instr->out(ofm_obj);
+ pad_instr->op(pad_op);
+
+ return pad_instr;
+ }
+
+private:
+ const coco::Padding2D *_pad;
+};
+
+PadInstrBuilder pad_instr_builder(const coco::Padding2D *pad) { return PadInstrBuilder{pad}; }
+
+} // namespace
+
+namespace
+{
+
+class AvgPoolRewritePass
+{
+private:
+ void runOnModule(coco::Module *m) const;
+
+public:
+ void runOnCode(enco::Code *) const;
+};
+
+void AvgPoolRewritePass::runOnModule(coco::Module *m) const
+{
+ // Lower AvgPool2D op that resides in Eval instruction
+ for (auto avgpool : candidates(m))
+ {
+ auto ins = avgpool->parent();
+ auto load = avgpool->arg()->asLoad();
+
+ assert(ins != nullptr);
+ assert(load != nullptr);
+ assert(avgpool->divisor() == coco::AvgPool2D::Divisor::Static);
+
+ if (empty(avgpool->pad()))
+ {
+ // NOTE If there is no padding, Static and PaddingExcluded schemes are equivalent
+ avgpool->divisor(coco::AvgPool2D::Divisor::PaddingExcluded);
+ }
+ else
+ {
+ // Before: Static AvgPool2D with Padding
+ // After: PadF; PaddingExcluded AvgPool2D without Padding
+
+ // Create PadF
+ auto ifm_obj = load->object()->asFeature();
+ assert(ifm_obj != nullptr);
+
+ auto pad_shape = shape_xform(avgpool->pad()).forward(ifm_obj->shape());
+ auto pad_bag = m->entity()->bag()->create(num_elements(pad_shape));
+ auto pad_obj = m->entity()->object()->create<coco::FeatureObject>();
+
+ pad_obj->bag(pad_bag);
+ pad_obj->layout(coco::FeatureLayouts::BHWC::create(pad_shape));
+
+ auto pad_instr = pad_instr_builder(avgpool->pad()).build(ifm_obj, pad_obj);
+
+ // Insert PadF before AvgPool2D
+ pad_instr->insertBefore(ins);
+
+ // Rewrite AvgPool2D as PaddingExcluded AvgPool2D without Padding
+ load->object(pad_obj);
+
+ avgpool->divisor(coco::AvgPool2D::Divisor::PaddingExcluded);
+ avgpool->pad()->top(0);
+ avgpool->pad()->bottom(0);
+ avgpool->pad()->left(0);
+ avgpool->pad()->right(0);
+ }
+ }
+}
+
+void AvgPoolRewritePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); }
+
+} // namespace
+
+namespace enco
+{
+
+void lower_avgpool(enco::Code *code)
+{
+ AvgPoolRewritePass pass;
+ pass.runOnCode(code);
+}
+
+} // namespace enco