[enco] tool to dump IR (initial version) (#2034)
author윤현식/동작제어Lab(SR)/Principal Engineer/삼성전자 <hyunsik.yoon@samsung.com>
Wed, 31 Oct 2018 07:34:29 +0000 (16:34 +0900)
committer박종현/동작제어Lab(SR)/Staff Engineer/삼성전자 <jh1302.park@samsung.com>
Wed, 31 Oct 2018 07:34:29 +0000 (16:34 +0900)
This tool prints IR info of caffe frontend.

Signed-off-by: Hyun Sik Yoon <hyunsik.yoon@samsung.com>
contrib/encodump/.FORMATCHECKED [new file with mode: 0644]
contrib/encodump/CMakeLists.txt [new file with mode: 0644]
contrib/encodump/src/Driver.cpp [new file with mode: 0644]
contrib/encodump/src/Dump.cpp [new file with mode: 0644]
contrib/encodump/src/Dump.h [new file with mode: 0644]

diff --git a/contrib/encodump/.FORMATCHECKED b/contrib/encodump/.FORMATCHECKED
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/contrib/encodump/CMakeLists.txt b/contrib/encodump/CMakeLists.txt
new file mode 100644 (file)
index 0000000..04e2fc3
--- /dev/null
@@ -0,0 +1,7 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_executable(encodump ${SOURCES})
+target_include_directories(encodump PRIVATE src)
+target_link_libraries(encodump enco_intf_frontend)
+target_link_libraries(encodump enco_core)
+target_link_libraries(encodump dl)
diff --git a/contrib/encodump/src/Driver.cpp b/contrib/encodump/src/Driver.cpp
new file mode 100644 (file)
index 0000000..3cf3f0a
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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 <enco/Frontend.h>
+#include <enco/Backend.h>
+
+#include <cmdline/View.h>
+
+#include <string>
+#include <vector>
+
+#include <functional>
+
+#include "Dump.h"
+
+namespace cmdline
+{
+
+// TODO Extract this helper class
+class Vector : public cmdline::View
+{
+public:
+  uint32_t size(void) const { return _args.size(); }
+
+public:
+  const char *at(uint32_t nth) const { return _args.at(nth).c_str(); }
+
+public:
+  Vector &append(const std::string &arg)
+  {
+    _args.emplace_back(arg);
+    return (*this);
+  }
+
+private:
+  std::vector<std::string> _args;
+};
+
+} // namespace cmdline
+
+namespace
+{
+
+class Zone
+{
+public:
+  Zone() = default;
+
+public:
+  const cmdline::View *args(void) const { return &_args; }
+
+public:
+  void append(const std::string &arg) { _args.append(arg); }
+
+private:
+  cmdline::Vector _args;
+};
+
+} // namespace
+
+#include <dlfcn.h>
+
+namespace
+{
+
+class FrontendFactory
+{
+public:
+  FrontendFactory(const std::string &path)
+  {
+    _handle = dlopen(path.c_str(), RTLD_LAZY);
+    assert(_handle != nullptr);
+  }
+
+public:
+  // Copy is not allowed to avoid double close
+  FrontendFactory(const FrontendFactory &) = delete;
+  FrontendFactory(FrontendFactory &&) = delete;
+
+public:
+  ~FrontendFactory() { dlclose(_handle); }
+
+private:
+  using Entry = std::unique_ptr<enco::Frontend> (*)(const cmdline::View &);
+
+private:
+  Entry entry(void) const
+  {
+    auto entry = reinterpret_cast<Entry>(dlsym(_handle, "make_frontend"));
+    assert(entry != nullptr);
+    return entry;
+  }
+
+public:
+  std::unique_ptr<enco::Frontend> make(const cmdline::View *args) const
+  {
+    auto fn = entry();
+    return fn(*args);
+  }
+
+private:
+  void *_handle;
+};
+
+} // namespace
+
+namespace
+{
+
+class FrontendZone : public Zone
+{
+public:
+  FrontendZone(const std::string &path) : _factory{path}
+  {
+    // DO NOTHING
+  }
+
+public:
+  const FrontendFactory *factory(void) const { return &_factory; }
+
+private:
+  FrontendFactory _factory;
+};
+
+} // namespace
+
+#include <nncc/foundation/Memory.h>
+
+#include <map>
+
+#include <iostream>
+#include <stdexcept>
+
+/**
+ * @brief Dump IR for given arguments
+ *
+ * Call example:
+ * $ ./build/contrib/encodump/encodump \
+ * --frontend build/contrib/enco/frontend/caffe/libenco_caffe_frontend.so \
+ * --frontend-arg build/contrib/enco/test/caffe/003.prototxt \
+ * --frontend-arg build/contrib/enco/test/caffe/003.caffemodel
+ */
+static int entry(int argc, char **argv)
+{
+  // Usage:
+  //  [Command] --frontend [Frontend .so path] --frontend-arg ...
+  std::unique_ptr<FrontendZone> frontend_zone;
+
+  // Simple argument parser (based on map)
+  std::map<std::string, std::function<void(const std::string &arg)>> argparse;
+
+  argparse["--frontend"] = [&](const std::string &path) {
+    frontend_zone = nncc::foundation::make_unique<FrontendZone>(path);
+  };
+
+  argparse["--frontend-arg"] = [&](const std::string &arg) { frontend_zone->append(arg); };
+
+  if (argc < 2)
+  {
+    std::cerr << "Usage:" << std::endl;
+    std::cerr << "[Command] --frontend [.so path]" << std::endl;
+    std::cerr << "          --frontend-arg [argument] ..." << std::endl;
+    return 255;
+  }
+
+  for (int n = 1; n < argc; n += 2)
+  {
+    const std::string tag{argv[n]};
+    const std::string arg{argv[n + 1]};
+
+    auto it = argparse.find(tag);
+
+    if (it == argparse.end())
+    {
+      std::cerr << "Option '" << tag << "' is not supported" << std::endl;
+      return 255;
+    }
+
+    it->second(arg);
+  }
+
+  assert(frontend_zone != nullptr);
+
+  auto frontend = frontend_zone->factory()->make(frontend_zone->args());
+
+  auto bundle = frontend->load();
+
+  // dump
+  dump(bundle.module());
+
+  // todo : dump data
+
+  return 0;
+}
+
+#ifdef NDEBUG
+int main(int argc, char **argv)
+{
+  try
+  {
+    return entry(argc, argv);
+  }
+  catch (const std::exception &e)
+  {
+    std::cerr << "ERROR: " << e.what() << std::endl;
+  }
+
+  return 255;
+}
+#else  // NDEBUG
+int main(int argc, char **argv)
+{
+  // NOTE main does not catch internal exceptions for debug build to make it easy to
+  //      check the stacktrace with a debugger
+  return entry(argc, argv);
+}
+#endif // !NDEBUG
diff --git a/contrib/encodump/src/Dump.cpp b/contrib/encodump/src/Dump.cpp
new file mode 100644 (file)
index 0000000..7e1e5f9
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * 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 "Dump.h"
+
+#include <iostream>
+
+std::string tab(int n) { return std::string(n * 2, ' '); }
+
+struct OpPrinter final : public coco::Op::Visitor<void>
+{
+public:
+  OpPrinter(std::ostream &os) : _os(os) {}
+
+public:
+  void visit(const coco::Load *op) override { _os << "; Load(" << op->object() << ")"; }
+
+  void visit(const coco::PadF *op) override
+  {
+    op->arg()->accept(this);
+    _os << "; PadF";
+  }
+
+  void visit(const coco::Conv2D *op) override
+  {
+    op->arg()->accept(this);
+    const coco::Padding2D *pad = op->pad();
+    const coco::Stride2D *stride = op->stride();
+
+    _os << "; Conv2D(ker: " << op->ker() << ", padding [T/B/L/R=" << pad->top() << ","
+        << pad->bottom() << "," << pad->left() << "," << pad->right() << "]"
+        << ", stride [V/H = " << stride->vertical() << "," << stride->horizontal() << "]"
+        << ")";
+  }
+
+  void visit(const coco::MaxPool2D *op) override
+  {
+    op->arg()->accept(this);
+    _os << "; MaxPool2D";
+  }
+
+  void visit(const coco::AvgPool2D *op) override
+  {
+    op->arg()->accept(this);
+    _os << "; AvgPool2D";
+  }
+
+  void visit(const coco::Add *op) override
+  {
+    op->left()->accept(this);
+    op->right()->accept(this);
+    _os << "; Add";
+  }
+
+  void visit(const coco::Mul *op) override
+  {
+    op->left()->accept(this);
+    op->right()->accept(this);
+    _os << "; Mul";
+  }
+
+  void visit(const coco::ReLU *op) override
+  {
+    op->arg()->accept(this);
+    _os << "; ReLU";
+  }
+
+private:
+  std::ostream &_os;
+};
+
+struct InstrPrinter final : public coco::Instr::Visitor<void>
+{
+public:
+  InstrPrinter() = delete;
+
+  InstrPrinter(int indent) : _indent(indent) {}
+
+  void visit(const coco::Eval *ins) override
+  {
+    std::cout << tab(_indent) << "Eval" << std::endl;
+    std::cout << tab(_indent) << "  out: " << ins->out() << std::endl;
+    std::cout << tab(_indent) << "  op:  ";
+    {
+      OpPrinter prn(std::cout);
+      ins->op()->accept(prn);
+    }
+    std::cout << std::endl;
+  }
+
+  void visit(const coco::Copy *ins) override
+  {
+    std::cout << tab(_indent) << "Copy" << std::endl;
+    std::cout << tab(_indent) << "  from: " << ins->from() << std::endl;
+    std::cout << tab(_indent) << "  into: " << ins->into() << std::endl;
+  }
+
+  void visit(const coco::Shuffle *ins) override
+  {
+    std::cout << tab(_indent) << "Shuffle" << std::endl;
+    std::cout << tab(_indent) << "  from: " << ins->from() << std::endl;
+    std::cout << tab(_indent) << "  into: " << ins->into() << std::endl;
+  }
+
+private:
+  int _indent;
+};
+
+void dump(const coco::Op *op)
+{
+  OpPrinter prn(std::cout);
+  op->accept(prn);
+}
+
+void dump(const coco::Instr *ins, int indent)
+{
+  std::cout << tab(indent) << "Inst:" << std::endl;
+
+  static InstrPrinter prn(indent + 1);
+
+  ins->accept(prn);
+}
+
+void dump(const coco::Block *B, int indent)
+{
+  std::cout << tab(indent) << "Block (index: " << B->index().value() << ")" << std::endl;
+  for (auto I = B->instr()->head(); I != nullptr; I = I->next())
+  {
+    dump(I, indent + 1);
+  }
+}
+
+void dump(const coco::BlockList *L, int indent)
+{
+  for (auto B = L->head(); B != nullptr; B = B->next())
+  {
+    dump(B, indent);
+  }
+}
+
+void dump(const coco::BagManager *l, int indent)
+{
+  for (auto n = 0; n < l->size(); ++n)
+  {
+    auto bag = l->at(n);
+    std::cout << tab(indent) << "%" << bag << " = Bag(size: " << bag->size() << ")" << std::endl;
+  }
+}
+void dump(const coco::ObjectManager *l, int indent)
+{
+  for (auto n = 0; n < l->size(); ++n)
+  {
+    auto obj = l->at(n);
+    std::cout << tab(indent) << "%" << obj << " = Object(bag: " << obj->bag() << ")" << std::endl;
+  }
+}
+
+template <typename T> void head(int indent);
+
+template <> void head<coco::Input>(int indent) { std::cout << tab(indent) << "Input: "; }
+
+template <> void head<coco::Output>(int indent) { std::cout << tab(indent) << "Output: "; }
+
+template <typename PtrItemT> void dump(const coco::PtrList<PtrItemT> *list, int indent)
+{
+  head<PtrItemT>(indent);
+  for (int n = 0; n < list->size(); n++)
+  {
+    const PtrItemT *item = list->at(n);
+    if (n != 0)
+      std::cout << ", ";
+    std::cout << "#" << n + 1 << ". name=" << item->name();
+  }
+  std::cout << std::endl;
+}
+
+void dump(const coco::Module *module)
+{
+  std::cout << "Module" << std::endl;
+
+  dump(module->block(), 1);
+  dump(module->input(), 1);
+  dump(module->output(), 1);
+}
diff --git a/contrib/encodump/src/Dump.h b/contrib/encodump/src/Dump.h
new file mode 100644 (file)
index 0000000..e7ea7d1
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef __DUMP_H__
+#define __DUMP_H__
+
+#include <coco/IR.h>
+
+void dump(const coco::Module *module);
+
+#endif // __DUMP_H__