--- /dev/null
+#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