[enco] Introduce NormalizePass (#1192)
author박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Mon, 27 Aug 2018 09:36:07 +0000 (18:36 +0900)
committerGitHub Enterprise <noreply-CODE@samsung.com>
Mon, 27 Aug 2018 09:36:07 +0000 (18:36 +0900)
This commit introduces NormalizePass which inserts data reordering
(Shuffle) instructions to make each instruction/operation compatible
with Android NN API.

Signed-off-by: Jonghyun Park <jh1302.park@samsung.com>
contrib/enco/core/src/Backend.cpp
contrib/enco/core/src/Transforms/Normalize.cpp [new file with mode: 0644]
contrib/enco/core/src/Transforms/Normalize.h [new file with mode: 0644]

index 5baf763..02a7e8d 100644 (file)
@@ -4,6 +4,7 @@
 #include "CppCode.h"
 
 #include "Transforms/Duplicate.h"
+#include "Transforms/Normalize.h"
 
 #include <stdexcept>
 
@@ -51,6 +52,10 @@ void Backend::compile(coco::Module *m, coco::Data *d)
   // that share the same bag as their underlying bag
   assert(!has_inout_bag(code.module()));
 
+  // Insert data ordering if necessary
+  NormalizePass normalize;
+  normalize.runOnCode(&code);
+
   // TODO Run various transforms over enco::Code
 
   _os << CppCode{&code} << std::endl;
diff --git a/contrib/enco/core/src/Transforms/Normalize.cpp b/contrib/enco/core/src/Transforms/Normalize.cpp
new file mode 100644 (file)
index 0000000..889885b
--- /dev/null
@@ -0,0 +1,215 @@
+#include "Normalize.h"
+
+#include "Normalize.h"
+
+#include <nncc/core/ADT/feature/Layout.h>
+#include <nncc/core/ADT/kernel/Layout.h>
+
+#include <nncc/core/ADT/feature/HWCLayout.h>
+#include <nncc/core/ADT/kernel/NHWCLayout.h>
+
+namespace
+{
+bool aligned(const coco::FeatureObject *o, const nncc::core::ADT::feature::Layout &l)
+{
+  const auto &shape = o->shape();
+
+  for (uint32_t row = 0; row < shape.height(); ++row)
+  {
+    for (uint32_t col = 0; col < shape.width(); ++col)
+    {
+      for (uint32_t ch = 0; ch < shape.depth(); ++ch)
+      {
+        if (o->at(ch, row, col).value() != l.offset(shape, ch, row, col))
+        {
+          return false;
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool aligned(const coco::KernelObject *o, const nncc::core::ADT::kernel::Layout &l)
+{
+  const auto &shape = o->shape();
+
+  for (uint32_t nth = 0; nth < shape.count(); ++nth)
+  {
+    for (uint32_t row = 0; row < shape.height(); ++row)
+    {
+      for (uint32_t col = 0; col < shape.width(); ++col)
+      {
+        for (uint32_t ch = 0; ch < shape.depth(); ++ch)
+        {
+          if (o->at(nth, ch, row, col).value() != l.offset(shape, nth, ch, row, col))
+          {
+            return false;
+          }
+        }
+      }
+    }
+  }
+
+  return true;
+}
+
+bool is_HWC_layout(const coco::FeatureObject *o)
+{
+  return aligned(o, nncc::core::ADT::feature::HWCLayout{});
+}
+
+class ShuffleGen : public coco::Instr::Mutator
+{
+public:
+  ShuffleGen(coco::Module *m) : _module{m}
+  {
+    // DO NOTHING
+  }
+
+private:
+  coco::FeatureObject *clone(const coco::FeatureObject *oldobj) const;
+  coco::Shuffle *create(const coco::FeatureObject *from, const coco::FeatureObject *into) const;
+
+public:
+  void mutate(coco::UnitF *unit) override;
+  void mutate(coco::Shuffle *) override;
+
+private:
+  coco::Module *const _module;
+};
+
+coco::FeatureObject *ShuffleGen::clone(const coco::FeatureObject *oldobj) const
+{
+  auto oldbag = oldobj->bag();
+
+  auto newbag = _module->entity()->bag()->create(oldbag->size());
+  auto newobj = _module->entity()->object()->create(oldobj->shape());
+
+  newobj->bag(newbag);
+  newobj->reorder<nncc::core::ADT::feature::HWCLayout>();
+
+  return newobj;
+}
+
+coco::Shuffle *ShuffleGen::create(const coco::FeatureObject *oldobj,
+                                  const coco::FeatureObject *newobj) const
+{
+  auto oldbag = oldobj->bag();
+  auto newbag = newobj->bag();
+
+  const auto &shape = oldobj->shape();
+
+  std::map<coco::ElemID /* NEW */, coco::ElemID /* OLD */> content;
+
+  for (uint32_t ch = 0; ch < shape.depth(); ++ch)
+  {
+    for (uint32_t row = 0; row < shape.height(); ++row)
+    {
+      for (uint32_t col = 0; col < shape.width(); ++col)
+      {
+        content[newobj->at(ch, row, col)] = oldobj->at(ch, row, col);
+      }
+    }
+  }
+
+  auto shuffle = _module->entity()->instr()->create<coco::Shuffle>();
+
+  shuffle->from(oldbag);
+  shuffle->into(newbag);
+
+  for (uint32_t n = 0; n < newbag->size(); ++n)
+  {
+    shuffle->at(n) = content[coco::ElemID{n}];
+  }
+
+  return shuffle;
+}
+
+void ShuffleGen::mutate(coco::UnitF *unit)
+{
+  auto m = unit->parent()->parent();
+
+#if 0
+  // Create a new bag B
+  // Create a new obj O
+  // Link O to B with HWC layout
+  //
+  // Shuffle elements from the exsiting bag to the newly allocated bag
+  //
+  // std::map<ElemID /* NEW */, ElemID /* OLD */> m;
+  // for (uint32_t ch = 0; ...)
+  //   for (uint32_t row = 0; ...)
+  //     for (uint32_t col = 0; ...)
+  //       m[new->at(ch, row, col)] = old->at(ch, row, col);
+  //
+  // auto shuffle = m->entity()->instr()->create<coco::Shuffle>();
+  //
+  // shffule->in(old_bag);
+  // shuffle->out(new_bag);
+  //
+  // for (uint32_t n = 0; ... )
+  //   shuffle->at(ElemID{n}) = m[ElemID{n}];
+#endif
+  if (unit->op()->asConv2D())
+  {
+    if (!is_HWC_layout(unit->ifm()))
+    {
+      auto oldobj = unit->ifm();
+      auto newobj = clone(oldobj);
+
+      auto shuffle = create(oldobj, newobj);
+      shuffle->insertBefore(unit);
+
+      unit->ifm(newobj);
+    }
+
+    if (!is_HWC_layout(unit->ofm()))
+    {
+      auto oldobj = unit->ofm();
+      auto newobj = clone(oldobj);
+
+      auto shuffle = create(newobj, oldobj);
+      shuffle->insertAfter(unit);
+
+      unit->ofm(newobj);
+    }
+
+    // Reorder Kernel as NHWC
+    auto ker_obj = unit->op()->asConv2D()->ker();
+    auto ker_bag = ker_obj->bag();
+
+    assert(ker_bag != nullptr);
+    assert(ker_bag->object()->size() == 1);
+    assert((ker_bag->isInput() == false) && (ker_bag->isOutput() == false));
+    ker_obj->reorder<nncc::core::ADT::kernel::NHWCLayout>();
+  }
+}
+
+void ShuffleGen::mutate(coco::Shuffle *)
+{
+  // DO NOTHING
+}
+
+} // namespace
+
+namespace enco
+{
+
+void NormalizePass::runOnModule(coco::Module *m) const
+{
+  ::ShuffleGen gen{m};
+
+  for (auto blk = m->block()->head(); blk; blk = blk->next())
+  {
+    for (auto ins = blk->instr()->head(); ins; ins = ins->next())
+    {
+      ins->accept(gen);
+    }
+  }
+}
+
+void NormalizePass::runOnCode(enco::Code *code) const { runOnModule(code->module()); }
+
+} // namespace enco
diff --git a/contrib/enco/core/src/Transforms/Normalize.h b/contrib/enco/core/src/Transforms/Normalize.h
new file mode 100644 (file)
index 0000000..a785e6c
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef __NORMALIZE_H__
+#define __NORMALIZE_H__
+
+#include "Code.h"
+
+namespace enco
+{
+
+/**
+ * @brief Insert data reordering if necessary
+ */
+class NormalizePass
+{
+private:
+  void runOnModule(coco::Module *m) const;
+
+public:
+  void runOnCode(enco::Code *) const;
+};
+
+} // namespace enco
+
+#endif // __NORMALIZE_H__