summaryrefslogtreecommitdiff
path: root/compiler/moco-tf
diff options
context:
space:
mode:
authorChunseok Lee <chunseok.lee@samsung.com>2020-04-23 14:45:49 +0900
committerChunseok Lee <chunseok.lee@samsung.com>2020-04-23 14:45:49 +0900
commite2ef8438a24f7c56a0744eb579a6e293ee2fbf8e (patch)
tree44a1a7951d168dd4370e13593ed03f4bc6d920c5 /compiler/moco-tf
parent302e6564a7a76109e1178207e44e45a58631c477 (diff)
downloadnnfw-e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e.tar.gz
nnfw-e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e.tar.bz2
nnfw-e2ef8438a24f7c56a0744eb579a6e293ee2fbf8e.zip
Imported Upstream version 1.4.0upstream/1.4.0submit/tizen/20200423.054851
Diffstat (limited to 'compiler/moco-tf')
-rw-r--r--compiler/moco-tf/CMakeLists.txt51
-rw-r--r--compiler/moco-tf/README.md57
-rw-r--r--compiler/moco-tf/doc/Conversion.md140
-rw-r--r--compiler/moco-tf/include/moco/tf/Frontend.h54
-rw-r--r--compiler/moco-tf/requires.cmake13
-rw-r--r--compiler/moco-tf/src/BroadcastHelper.cpp226
-rw-r--r--compiler/moco-tf/src/BroadcastHelper.h76
-rw-r--r--compiler/moco-tf/src/BroadcastHelper.test.cpp88
-rw-r--r--compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp49
-rw-r--r--compiler/moco-tf/src/CanonicalEltwiseInputConnector.h60
-rw-r--r--compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp35
-rw-r--r--compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp114
-rw-r--r--compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp109
-rw-r--r--compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp160
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp127
-rw-r--r--compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp371
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h45
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp132
-rw-r--r--compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp137
-rw-r--r--compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h45
-rw-r--r--compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp78
-rw-r--r--compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp111
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp34
-rw-r--r--compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp31
-rw-r--r--compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp34
-rw-r--r--compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp100
-rw-r--r--compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h45
-rw-r--r--compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp102
-rw-r--r--compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp34
-rw-r--r--compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp70
-rw-r--r--compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp70
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp169
-rw-r--r--compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp150
-rw-r--r--compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp78
-rw-r--r--compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp68
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp86
-rw-r--r--compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h49
-rw-r--r--compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp71
-rw-r--r--compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp34
-rw-r--r--compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp74
-rw-r--r--compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp70
-rw-r--r--compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h47
-rw-r--r--compiler/moco-tf/src/Canonicalizer.cpp142
-rw-r--r--compiler/moco-tf/src/Canonicalizer.h36
-rw-r--r--compiler/moco-tf/src/Canonicalizer.test.cpp33
-rw-r--r--compiler/moco-tf/src/CodecHelper.h74
-rw-r--r--compiler/moco-tf/src/Convert.cpp34
-rw-r--r--compiler/moco-tf/src/Convert.h31
-rw-r--r--compiler/moco-tf/src/Convert.test.cpp29
-rw-r--r--compiler/moco-tf/src/Frontend.cpp277
-rw-r--r--compiler/moco-tf/src/Frontend.test.cpp84
-rw-r--r--compiler/moco-tf/src/Knob.cpp123
-rw-r--r--compiler/moco-tf/src/Knob.h47
-rw-r--r--compiler/moco-tf/src/Knob.lst39
-rw-r--r--compiler/moco-tf/src/LogHelper.cpp82
-rw-r--r--compiler/moco-tf/src/LogHelper.h73
-rw-r--r--compiler/moco-tf/src/Op/COpCall.cpp126
-rw-r--r--compiler/moco-tf/src/Op/COpCall.h46
-rw-r--r--compiler/moco-tf/src/Op/COpCall.test.cpp121
-rw-r--r--compiler/moco-tf/src/Optimizer.cpp90
-rw-r--r--compiler/moco-tf/src/Optimizer.h36
-rw-r--r--compiler/moco-tf/src/Optimizer.test.cpp87
-rw-r--r--compiler/moco-tf/src/ProgressReporter.cpp88
-rw-r--r--compiler/moco-tf/src/ProgressReporter.h56
-rw-r--r--compiler/moco-tf/src/SimpleNodeTransform.h64
-rw-r--r--compiler/moco-tf/src/SimpleNodeTransform.test.cpp56
-rw-r--r--compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h117
-rw-r--r--compiler/moco-tf/src/TFFormattedGraph.cpp400
-rw-r--r--compiler/moco-tf/src/TFFormattedGraph.h59
-rw-r--r--compiler/moco-tf/src/TFOptimizer.cpp81
-rw-r--r--compiler/moco-tf/src/TFOptimizer.h36
-rw-r--r--compiler/moco-tf/src/TFOptimizer.test.cpp33
-rw-r--r--compiler/moco-tf/src/TFReduceCanonicalzeHelper.h118
-rw-r--r--compiler/moco-tf/src/TestHelper.h113
-rw-r--r--compiler/moco-tf/src/TestHelper.test.cpp121
-rw-r--r--compiler/moco-tf/src/Transform.cpp35
-rw-r--r--compiler/moco-tf/src/Transform.h44
-rw-r--r--compiler/moco-tf/src/Transform.test.cpp46
-rw-r--r--compiler/moco-tf/src/Transforms.h26
-rw-r--r--compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp56
-rw-r--r--compiler/moco-tf/src/Transforms/ShapeInferencePass.h44
-rw-r--r--compiler/moco-tf/src/Transforms/TypeInferencePass.cpp54
-rw-r--r--compiler/moco-tf/src/Transforms/TypeInferencePass.h44
105 files changed, 8029 insertions, 0 deletions
diff --git a/compiler/moco-tf/CMakeLists.txt b/compiler/moco-tf/CMakeLists.txt
new file mode 100644
index 000000000..5516388a4
--- /dev/null
+++ b/compiler/moco-tf/CMakeLists.txt
@@ -0,0 +1,51 @@
+if(NOT TARGET mio_tf)
+ return()
+endif(NOT TARGET mio_tf)
+
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(moco_tf_frontend SHARED ${SOURCES})
+target_include_directories(moco_tf_frontend PRIVATE src)
+target_include_directories(moco_tf_frontend PUBLIC include)
+target_link_libraries(moco_tf_frontend PUBLIC loco)
+target_link_libraries(moco_tf_frontend PUBLIC moco_lang)
+target_link_libraries(moco_tf_frontend PUBLIC moco_import)
+target_link_libraries(moco_tf_frontend PUBLIC moco_pass)
+target_link_libraries(moco_tf_frontend PUBLIC mio_tf)
+target_link_libraries(moco_tf_frontend PRIVATE moco_service)
+target_link_libraries(moco_tf_frontend PRIVATE moco_support)
+target_link_libraries(moco_tf_frontend PRIVATE bino)
+target_link_libraries(moco_tf_frontend PRIVATE fipe)
+target_link_libraries(moco_tf_frontend PRIVATE locop)
+target_link_libraries(moco_tf_frontend PRIVATE stdex)
+target_link_libraries(moco_tf_frontend PRIVATE moco_log)
+target_link_libraries(moco_tf_frontend PRIVATE pepper_str)
+target_link_libraries(moco_tf_frontend PRIVATE pepper_strcast)
+target_link_libraries(moco_tf_frontend PRIVATE locomotiv)
+target_link_libraries(moco_tf_frontend PRIVATE plier_tf)
+target_link_libraries(moco_tf_frontend PRIVATE locoex_customop)
+target_link_libraries(moco_tf_frontend PRIVATE logo)
+target_link_libraries(moco_tf_frontend PRIVATE oops)
+install(TARGETS moco_tf_frontend DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+add_executable(moco_tf_frontend_test ${TESTS})
+target_include_directories(moco_tf_frontend_test PRIVATE src)
+target_link_libraries(moco_tf_frontend_test gtest_main)
+target_link_libraries(moco_tf_frontend_test bino)
+target_link_libraries(moco_tf_frontend_test fipe)
+target_link_libraries(moco_tf_frontend_test locop)
+target_link_libraries(moco_tf_frontend_test moco_log)
+target_link_libraries(moco_tf_frontend_test moco_tf_frontend)
+target_link_libraries(moco_tf_frontend_test stdex)
+target_link_libraries(moco_tf_frontend_test plier_tf)
+target_link_libraries(moco_tf_frontend_test locoex_customop)
+target_link_libraries(moco_tf_frontend_test logo)
+add_test(moco_tf_frontend_test moco_tf_frontend_test)
diff --git a/compiler/moco-tf/README.md b/compiler/moco-tf/README.md
new file mode 100644
index 000000000..add1159e1
--- /dev/null
+++ b/compiler/moco-tf/README.md
@@ -0,0 +1,57 @@
+# moco-tf
+
+_moco-tf_ translates a TensorFlow model into _loco_
+
+## Purpose
+
+_moco-tf_ is to convert TensorFlow generated model file to in-memory _loco_ IR Graph.
+
+## How to use
+
+```cxx
+#include <moco/tf/Frontend.h>
+
+...
+
+ ::moco::tf::Frontend moco;
+
+ std::string pb_path = "path_to_pb_file_to_load";
+
+ auto loco_graph = moco.load(sig, pb_path, ::moco::tf::Frontend::FileType::Binary);
+```
+
+## Dependency
+
+Please refer [requires.cmake](./requires.cmake) for dependant modules.
+
+## Naming rules
+
+### TensorFlow node names
+
+Use `REGISTER_OP` argument used in TensorFlow source `core` folder.
+
+```
+cd tensorflow/core
+grep -Rn "REGISTER_OP"
+```
+
+To see single Op, `Conv2D` for example
+```
+cd tensorflow/core
+grep -Rn "REGISTER_OP" | grep "Conv2D"
+```
+
+### Names related with TensorFlow nodes
+
+Like `GraphBuilder` and `Canonicalization`, TensorFlow node names can be used as
+prefix or suffix.
+
+- `Conv2DGraphBuilder`
+- `Conv2DCanonicalizier`
+
+### TensorFlow Dialect IR
+
+Use `TF` prefix with TensorFlow Dialect node names
+
+- `TFAvgPool`
+- `TFConv2D`
diff --git a/compiler/moco-tf/doc/Conversion.md b/compiler/moco-tf/doc/Conversion.md
new file mode 100644
index 000000000..08551cc3c
--- /dev/null
+++ b/compiler/moco-tf/doc/Conversion.md
@@ -0,0 +1,140 @@
+This document outlines how to express each TensorFlow operation on top of _loco_
+
+**CAUTION** All the python examples below are written in Python 3 with TensorFlow v1.13.
+
+**DISCLAIMER** _loco_ does not support named values, but all the below _loco_ examples assign "name" to each value to make it easy to read.
+
+### Placeholder
+
+**Placeholder** in _TensorFlow_ corresponds to **Pull** in _loco_.
+
+_Python_:
+```python
+import tensorflow as tf
+input = tf.placeholder(dtype=tf.float32, shape=[3, 4], name='input')
+print(tf.get_default_graph().as_graph_def())
+```
+
+API reference: [tf.placeholder](https://www.tensorflow.org/versions/r1.13/api_docs/python/tf)
+
+_TensorFlow_
+```prototext
+node {
+ name: "input"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim { size: 3 }
+ dim { size: 4 }
+ }
+ }
+ }
+}
+```
+
+_loco_:
+```
+%input = Pull(dtype: FLOAT32, shape: [3, 4])
+Push(%input)
+```
+
+### Identity
+
+**Identity** in _TensorFlow_ corresponds to **Forward** in _loco_.
+
+_Python_:
+```python
+import tensorflow as tf
+input = tf.placeholder(dtype=tf.float32, shape=[3, 4])
+ident = tf.identity(input)
+print(tf.get_default_graph().as_graph_def())
+```
+
+API reference: [tf.identity](https://www.tensorflow.org/api_docs/python/tf/identity)
+
+_TensorFlow_:
+```
+node {
+ name: "Placeholder"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim { size: 3 }
+ dim { size: 4 }
+ }
+ }
+ }
+}
+node {
+ name: "Identity"
+ op: "Identity"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+}
+```
+
+_loco_:
+```
+%input = Pull(dtype: FLOAT32, shape: [3, 4])
+%ident = Forward(%input)
+Push(%ident)
+```
+
+### Const
+
+**Const** in _TensorFlow_ corresponds to **ConstGen** in _loco_.
+
+_Python_:
+```python
+import tensorflow as tf
+constant = tf.constant(value=[1.0], dtype=tf.float32, shape=[3, 4])
+tf.get_default_graph().as_graph_def()
+```
+
+API reference: [tf.constant](https://www.tensorflow.org/versions/r1.13/api_docs/python/tf/constant)
+
+_TensorFlow_:
+```
+node {
+ name: "Const"
+ op: "Const"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape {
+ dim { size: 3 }
+ dim { size: 4 }
+ }
+ float_val: 1.0
+ }
+ }
+ }
+}
+```
+
+_loco_:
+```
+%constant = ConstGen(dtype: FLOAT32, shape: [3, 4], data: ...);
+Push(%constant)
+```
diff --git a/compiler/moco-tf/include/moco/tf/Frontend.h b/compiler/moco-tf/include/moco/tf/Frontend.h
new file mode 100644
index 000000000..6914fdd38
--- /dev/null
+++ b/compiler/moco-tf/include/moco/tf/Frontend.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TENSORFLOW_FRONTEND_H__
+#define __MOCO_TENSORFLOW_FRONTEND_H__
+
+#include <moco/Import/ModelSignature.h>
+
+#include <loco.h>
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+namespace moco
+{
+namespace tf
+{
+
+class Frontend
+{
+public:
+ enum class FileType
+ {
+ Text,
+ Binary,
+ };
+
+public:
+ Frontend();
+
+public:
+ std::unique_ptr<loco::Graph> load(const ModelSignature &, const char *, FileType) const;
+ std::unique_ptr<loco::Graph> load(const ModelSignature &, std::istream *, FileType) const;
+
+private:
+ std::unique_ptr<loco::Graph> import(const ModelSignature &, tensorflow::GraphDef &) const;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TENSORFLOW_FRONTEND_H__
diff --git a/compiler/moco-tf/requires.cmake b/compiler/moco-tf/requires.cmake
new file mode 100644
index 000000000..751192fff
--- /dev/null
+++ b/compiler/moco-tf/requires.cmake
@@ -0,0 +1,13 @@
+require("fipe")
+require("loco")
+require("moco")
+require("locop")
+require("stdex")
+require("moco-log")
+require("pepper-strcast")
+require("locomotiv")
+require("mio-tf")
+require("plier-tf")
+require("locoex-customop")
+require("logo")
+require("oops")
diff --git a/compiler/moco-tf/src/BroadcastHelper.cpp b/compiler/moco-tf/src/BroadcastHelper.cpp
new file mode 100644
index 000000000..fc058c141
--- /dev/null
+++ b/compiler/moco-tf/src/BroadcastHelper.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2019 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 "BroadcastHelper.h"
+
+#include <loco/IR/Nodes.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+
+namespace
+{
+
+class NodeWithTensorShape
+{
+public:
+ NodeWithTensorShape() = default;
+
+public:
+ NodeWithTensorShape(loco::Node *node, const loco::TensorShape &shape) : _node{node}, _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ loco::Node *node(void) const { return _node; }
+ const loco::TensorShape &shape(void) const { return _shape; }
+
+private:
+ loco::Node *_node = nullptr;
+ loco::TensorShape _shape;
+};
+
+NodeWithTensorShape glue(loco::Node *node, const loco::TensorShape &shape)
+{
+ return NodeWithTensorShape(node, shape);
+}
+
+/**
+ * @brief Create a higher-rank TensorShape following NumPy broadcasting semantics
+ *
+ * HOW TO USE:
+ *
+ * auto expanded_tensor_shape = expand(tensor_shape).to(N);
+ */
+class TensorShapeExpander
+{
+public:
+ TensorShapeExpander(const loco::TensorShape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ loco::TensorShape to(uint32_t output_rank)
+ {
+ auto const &input_shape = _shape;
+ uint32_t const input_rank = input_shape.rank();
+
+ assert(input_rank <= output_rank && "Cannot shrink rank");
+ uint32_t const axis_shift = output_rank - input_rank;
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(output_rank);
+ for (uint32_t axis = 0; axis < output_rank; ++axis)
+ {
+ output_shape.dim(axis) = (axis < axis_shift) ? 1 : input_shape.dim(axis - axis_shift);
+ }
+
+ return output_shape;
+ }
+
+private:
+ const loco::TensorShape _shape;
+};
+
+TensorShapeExpander expand(const loco::TensorShape &shape) { return TensorShapeExpander{shape}; }
+
+/**
+ * @brief Create a rank-expanded node (if required)
+ */
+class ExpandRankFunctor final
+{
+public:
+ ExpandRankFunctor(uint32_t rank) : _rank{rank}
+ {
+ // DO NOTHING
+ }
+
+public:
+ NodeWithTensorShape operator()(const NodeWithTensorShape &in) const
+ {
+ auto const input_node = in.node();
+ auto const input_shape = in.shape();
+ auto const input_rank = input_shape.rank();
+
+ uint32_t const expected_rank = _rank;
+
+ assert(input_rank <= expected_rank);
+ if (input_rank == expected_rank)
+ {
+ // Nothing to expand
+ return in;
+ }
+
+ auto g = input_node->graph();
+ assert(g != nullptr);
+
+ auto output_shape = expand(input_shape).to(expected_rank);
+ auto output_node = g->nodes()->create<loco::FixedReshape>();
+
+ output_node->input(input_node);
+ output_node->rank(expected_rank);
+ for (uint32_t axis = 0; axis < expected_rank; ++axis)
+ {
+ output_node->dim(axis) = output_shape.dim(axis);
+ }
+
+ return glue(output_node, output_shape);
+ }
+
+private:
+ uint32_t _rank;
+};
+
+ExpandRankFunctor expand_rank_to(uint32_t rank) { return ExpandRankFunctor{rank}; }
+
+/**
+ * @brief Create a dimension-expanded node (if required)
+ */
+class ExpandDimsFunctor final
+{
+public:
+ ExpandDimsFunctor(const loco::TensorShape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ NodeWithTensorShape operator()(const NodeWithTensorShape &in) const
+ {
+ auto const input_node = in.node();
+ auto const input_shape = in.shape();
+ const auto &output_shape = _shape;
+
+ assert(input_shape.rank() == output_shape.rank());
+
+ if (input_shape == output_shape)
+ {
+ // Nothing to expand
+ return in;
+ }
+
+ uint32_t const rank = output_shape.rank();
+
+ auto g = input_node->graph();
+ assert(g != nullptr);
+
+ auto output_node = g->nodes()->create<loco::TensorBroadcast>();
+
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ auto input_dim = input_shape.dim(axis);
+ auto output_dim = output_shape.dim(axis);
+
+ assert(input_dim.known() and output_dim.known());
+
+ if (!(input_dim == output_dim))
+ {
+ assert(input_dim == 1);
+ output_node->mapping()->dim(axis) = output_dim;
+ }
+ }
+
+ output_node->input(input_node);
+
+ return glue(output_node, output_shape);
+ }
+
+private:
+ loco::TensorShape _shape;
+};
+
+ExpandDimsFunctor expand_dims_as(const loco::TensorShape &shape)
+{
+ return ExpandDimsFunctor{shape};
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+loco::Node *BroadcastFunctor::build(loco::Node *node, const loco::TensorShape &shape) const
+{
+ // clang-format off
+ return glue(node, shape)
+ | expand_rank_to(_shape.rank())
+ | expand_dims_as(_shape)
+ | [] (const NodeWithTensorShape &in) { return in.node(); };
+ // clang-format on
+}
+
+loco::Node *BroadcastFunctor::build(loco::Node *node) const
+{
+ return build(node, loco::shape_get(node).as<loco::TensorShape>());
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/BroadcastHelper.h b/compiler/moco-tf/src/BroadcastHelper.h
new file mode 100644
index 000000000..6238ad269
--- /dev/null
+++ b/compiler/moco-tf/src/BroadcastHelper.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __BROADCAST_HELPER_H__
+#define __BROADCAST_HELPER_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Dimension.h>
+#include <loco/IR/TensorShape.h>
+
+#include <bino.h>
+#include <fipe.h> // include "fipe.h" for clients
+
+namespace moco
+{
+namespace tf
+{
+
+class BroadcastFunctor final
+{
+public:
+ BroadcastFunctor(const loco::TensorShape &shape) : _shape{shape}
+ {
+ // DO NOTHING
+ }
+
+public:
+ loco::Node *build(loco::Node *in_node, const loco::TensorShape &in_shape) const;
+
+ loco::Node *operator()(loco::Node *in_node, const loco::TensorShape &in_shape) const
+ {
+ return build(in_node, in_shape);
+ }
+
+ // This method assumes the followings:
+ // - loco::shape_known(node) returns true, and
+ // - loco::shape_get(node).domain() is loco::Domain::Tensor
+ loco::Node *build(loco::Node *node) const;
+
+ loco::Node *operator()(loco::Node *node) const { return build(node); }
+
+private:
+ loco::TensorShape _shape;
+};
+
+/**
+ * @brief Create a broadcasted node
+ *
+ * First, append canonical.FixedReshape if rank expansion is required.
+ * Then, append canonical.TensorBroadcast if dimension expansion is required
+ *
+ * This mimics "tf.broadcast_to" API in TensorFlow.
+ */
+static inline auto broadcast_to(const loco::TensorShape &shape)
+ -> decltype(bino::transform_both(std::declval<BroadcastFunctor>()))
+{
+ return bino::transform_both(BroadcastFunctor{shape});
+}
+
+} // namespace tf
+} // namespace moco
+
+#endif // __BROADCAST_HELPER_H__
diff --git a/compiler/moco-tf/src/BroadcastHelper.test.cpp b/compiler/moco-tf/src/BroadcastHelper.test.cpp
new file mode 100644
index 000000000..a6cbd719a
--- /dev/null
+++ b/compiler/moco-tf/src/BroadcastHelper.test.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019 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 "BroadcastHelper.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(BroadcastFunctorTest, expand_rank)
+{
+ // Broadcast Tensor<3> as Tensor<1 x 3>
+ auto g = loco::make_graph();
+
+ auto input = g->inputs()->create();
+
+ auto pull = g->nodes()->create<loco::Pull>();
+ pull->index(0);
+
+ loco::TensorShape current_shape;
+ {
+ current_shape.rank(1);
+ current_shape.dim(0) = 3;
+ }
+
+ loco::TensorShape expected_shape;
+ {
+ expected_shape.rank(2);
+ expected_shape.dim(0) = 1;
+ expected_shape.dim(1) = 3;
+ }
+
+ moco::tf::BroadcastFunctor functor{expected_shape};
+
+ auto node = functor.build(pull, current_shape);
+
+ ASSERT_EQ(node->opnum(), static_cast<uint32_t>(loco::CanonicalOpcode::FixedReshape));
+ ASSERT_EQ(node->arg(0), pull);
+}
+
+TEST(BroadcastFunctorTest, expand_dims)
+{
+ // Broadcast Tensor<1> as Tensor<3>
+ auto g = loco::make_graph();
+
+ auto input = g->inputs()->create();
+
+ auto pull = g->nodes()->create<loco::Pull>();
+ pull->index(0);
+
+ loco::TensorShape current_shape;
+ {
+ current_shape.rank(1);
+ current_shape.dim(0) = 1;
+ }
+
+ loco::TensorShape expected_shape;
+ {
+ expected_shape.rank(1);
+ expected_shape.dim(0) = 3;
+ }
+
+ moco::tf::BroadcastFunctor functor{expected_shape};
+
+ auto node = functor.build(pull, current_shape);
+
+ ASSERT_EQ(node->opnum(), static_cast<uint32_t>(loco::CanonicalOpcode::TensorBroadcast));
+ ASSERT_EQ(node->arg(0), pull);
+
+ auto tensor_broadcast = dynamic_cast<loco::TensorBroadcast *>(node);
+
+ ASSERT_NE(tensor_broadcast, nullptr);
+ ASSERT_TRUE(tensor_broadcast->mapping()->defined(0));
+ ASSERT_EQ(tensor_broadcast->mapping()->dim(0), 3);
+}
diff --git a/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp
new file mode 100644
index 000000000..adeae39de
--- /dev/null
+++ b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 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 "CanonicalEltwiseInputConnector.h"
+
+#include <loco/IR/Nodes.h>
+
+namespace moco
+{
+namespace tf
+{
+namespace eltwise
+{
+namespace binary
+{
+
+template <typename NodeTy> void InputConnector<NodeTy>::operator()(const NodePair &p) const
+{
+ _node->lhs(p.first);
+ _node->rhs(p.second);
+}
+
+#define INSTANTIATE(OP) template void InputConnector<loco::OP>::operator()(const NodePair &) const;
+
+INSTANTIATE(EltwiseAdd);
+INSTANTIATE(EltwiseSub);
+INSTANTIATE(EltwiseMax);
+INSTANTIATE(EltwiseMul);
+INSTANTIATE(EltwiseDiv);
+
+#undef INSTANTIATE
+
+} // namespace binary
+} // namespace eltwise
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/CanonicalEltwiseInputConnector.h b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.h
new file mode 100644
index 000000000..a50a5011c
--- /dev/null
+++ b/compiler/moco-tf/src/CanonicalEltwiseInputConnector.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __CANONICAL_ELTWISE_INPUT_CONNECTOR_H__
+#define __CANONICAL_ELTWISE_INPUT_CONNECTOR_H__
+
+#include <loco/IR/Node.h>
+
+#include <utility>
+
+namespace moco
+{
+namespace tf
+{
+namespace eltwise
+{
+namespace binary
+{
+
+using NodePair = std::pair<loco::Node *, loco::Node *>;
+
+template <typename NodeTy> class InputConnector
+{
+public:
+ InputConnector(NodeTy *node) : _node{node}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void operator()(const NodePair &p) const;
+
+private:
+ NodeTy *_node;
+};
+
+template <typename NodeTy> InputConnector<NodeTy> connect_to(NodeTy *node)
+{
+ return InputConnector<NodeTy>{node};
+}
+
+} // namespace binary
+} // namespace eltwise
+} // namespace tf
+} // namespace moco
+
+#endif // __CANONICAL_ELTWISE_INPUT_CONNECTOR_H__
diff --git a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp
new file mode 100644
index 000000000..8028a870c
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 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 "AddCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+
+#include "TFEltwiseBinaryCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool AddCanonicalizer::transform(TFAdd *node) const
+{
+ return canonicalize_eltwise_binary_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h
new file mode 100644
index 000000000..53ba9ed58
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/AddCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_ADD_CANONICALIZER_H__
+#define __MOCO_TF_ADD_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFAdd to Canonical EltwiseAdd
+ */
+class AddCanonicalizer : public SimpleNodeTransform<TFAdd>
+{
+public:
+ const char *name(void) const final { return "AddCanonicalizer"; }
+
+public:
+ bool transform(TFAdd *node) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_ADD_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp
new file mode 100644
index 000000000..e07a4f64f
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2019 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 "AvgPoolCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include "CodecHelper.h"
+
+#include <loco/IR/NodeShape.h>
+
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_avgpool2d(loco::Graph *graph, moco::TFAvgPool *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFAvgPool node with Canonical FeatureEncode +
+ * AvgPool2D + FeatureDecode
+ *
+ * Before
+ * A -- TFAvgPool -- C
+ *
+ * After
+ * +- TFAvgPool --
+ * |
+ * A -+- FeatureEncode -- AvgPool2D -- FeatureDecode -- C
+ *
+ * Where
+ * A : value of TFAvgPool
+ * C : a node that uses TFAvgPool as an input
+ * TFAvgPool is disconnected from other nodes
+ */
+
+ auto data_layout = plier::tf::as_data_layout(node->data_layout());
+
+ auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+ auto avgPool2d_node = graph->nodes()->create<loco::AvgPool2D>();
+ auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+ set_feature_enc(feature_enc, data_layout);
+ set_feature_dec(feature_dec, data_layout);
+
+ avgPool2d_node->convention(loco::AvgPool2D::Convention::Valid);
+
+ auto value_shape = moco::node_shape(node->value());
+ assert(value_shape.domain() != loco::Domain::Unknown);
+
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(node->ksize(), node->data_layout());
+
+ moco::Padding2DInference infer_padding2d;
+
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
+
+ auto input_feature_shape = moco::as_feature_shape(value_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+
+ *avgPool2d_node->pad() = infer_padding2d(input_plane_shape);
+ *avgPool2d_node->stride() = node_stride;
+ *avgPool2d_node->window() = node_window;
+
+ INFO(l) << "Canonicalize TFAvgPool pad = T " << avgPool2d_node->pad()->top() << ", L "
+ << avgPool2d_node->pad()->left() << ", B " << avgPool2d_node->pad()->bottom() << ", R "
+ << avgPool2d_node->pad()->right() << std::endl;
+
+ // update graph
+ auto node_A = node->value();
+
+ // update connections
+ feature_enc->input(node_A);
+ avgPool2d_node->ifm(feature_enc);
+ feature_dec->input(avgPool2d_node);
+
+ // replace node
+ replace(node).with(feature_dec);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool AvgPoolCanonicalizer::transform(TFAvgPool *node) const
+{
+ return canonicalize_avgpool2d(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h
new file mode 100644
index 000000000..e9c56c868
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/AvgPoolCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_AVGPOOL_CANONICALIZER_H__
+#define __MOCO_TF_AVGPOOL_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFAvgPool to Canonical AvgPool2D
+ */
+class AvgPoolCanonicalizer : public SimpleNodeTransform<moco::TFAvgPool>
+{
+public:
+ const char *name(void) const final { return "AvgPoolCanonicalizer"; }
+
+public:
+ bool transform(TFAvgPool *node) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_AVGPOOL_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp
new file mode 100644
index 000000000..a5568ce1a
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2019 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 "BiasAddCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Names.h>
+#include <moco/Log.h>
+#include <plier/tf/Convert.h>
+
+namespace
+{
+using plier::tf::DataLayout;
+
+bool canonicalize_biasadd(loco::Graph *graph, moco::TFBiasAdd *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFBiasAdd node with Canonical BiasEncode + TensorBiasAdd
+ *
+ * Before
+ * A -- TFBiasAdd - C
+ * B -/
+ *
+ * After
+ * A -- TFBiasAdd -
+ * B -/
+ * A --------------- TensorBiasAdd - C
+ * B - BiasEncode -/
+ *
+ * Where
+ * A : value of TFBiasAdd
+ * B : bias of TFBiasAdd
+ * C : a node that uses TFBiasAdd as an input
+ * TFBiasAdd is disconnected from node C
+ * A and B are drawn twice to simplify the diagram
+ */
+
+ INFO(l) << "TFNodeCanonicalize TFBiasAdd begin";
+
+ // tensorflow data_format: one of NHWC or NCHW.
+ auto data_layout = plier::tf::as_data_layout(node->data_layout());
+
+ // creating loco nodes
+ auto bias_enc = graph->nodes()->create<loco::BiasEncode>();
+
+ auto bias_add = graph->nodes()->create<loco::TensorBiasAdd>();
+ {
+ if (data_layout == DataLayout::NHWC)
+ {
+ INFO(l) << "TFNodeCanonicalize TFBiasAdd axis 3";
+ bias_add->axis(3);
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ INFO(l) << "TFNodeCanonicalize TFBiasAdd axis 1";
+ bias_add->axis(1); // Channel
+ // Note: the following descrition of TF 1.13 at
+ // https://www.tensorflow.org/api_docs/python/tf/nn/bias_add seems wrong:
+ // "bias: A 1-D Tensor with size matching the last dimension of value."
+ // because providing the size of W (last dimension) to bias throws an error with TensorFlow
+ }
+ }
+
+ auto node_A = node->value();
+ auto node_B = node->bias();
+
+ // update connections
+ bias_add->value(node_A);
+ bias_add->bias(bias_enc);
+ bias_enc->input(node_B);
+
+ // replace old with new : about C in above note
+ replace(node).with(bias_add);
+
+ INFO(l) << "TFNodeCanonicalize TFBiasAdd done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool BiasAddCanonicalizer::transform(TFBiasAdd *node) const
+{
+ return canonicalize_biasadd(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h
new file mode 100644
index 000000000..ff4032ca9
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/BiasAddCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_BIASADD_CANONICALIZER_H__
+#define __MOCO_TF_BIASADD_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFBiasAdd to Canonical BiasAdd
+ */
+class BiasAddCanonicalizer final : public SimpleNodeTransform<moco::TFBiasAdd>
+{
+public:
+ const char *name(void) const final { return "BiasAddCanonicalizer"; }
+
+public:
+ bool transform(TFBiasAdd *node) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_BIASADD_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp
new file mode 100644
index 000000000..b59a3f3d7
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.cpp
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2019 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 "ConcatV2Canonicalizer.h"
+#include "LogHelper.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include <moco/Log.h>
+
+#include <loco/Service/ShapeInference.h>
+
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+namespace
+{
+
+using namespace moco::tf;
+
+bool scalar_value(moco::TFConst *node, int32_t &ret)
+{
+ auto nodeshape = node_shape(node);
+ if (!(node->dtype() == loco::DataType::S32))
+ return false;
+
+ auto tensor_shape = nodeshape.as<loco::TensorShape>();
+ if (!(tensor_shape.rank() == 0 || tensor_shape.rank() == 1))
+ return false;
+
+ ret = node->at<loco::DataType::S32>(0);
+
+ return true;
+}
+
+bool canonicalize_concat(loco::Graph *graph, moco::TFConcatV2 *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFConcatV2 node with (series of) Canonical
+ * TensorConcat. Below diagram is an example of three inputs
+ *
+ * Before
+ * A --- TFConcatV2 -- C
+ * B --/
+ * N --/
+ * X --/
+ * After
+ * A --- TFConcatV2
+ * B --/
+ * N --/
+ * X --/
+ * A --- TensorConcat -- TensorConcat -- C
+ * B --/ /
+ * N -----------------/
+ *
+ * Where
+ * A : first value of TFConcatV2
+ * B : second value of TFConcatV2
+ * N : third or N'th value of TFConcatV2
+ * X : axis node of TFConcatV2
+ * C : a node that uses TFConcatV2 as an input
+ * TFConcatV2 is disconnected from C
+ * To simplify the diagram in 'After', A, B, N are drawn
+ * multiple times but they are same nodes.
+ */
+
+ const int num_values = node->num_values();
+ assert(num_values >= 2);
+
+ // get axis absolute value
+ auto value_a = node->values(0);
+ if (!loco::shape_known(value_a))
+ return false;
+
+ uint32_t node_rank = 0;
+ {
+ auto value_a_shape = moco::node_shape(value_a);
+ assert(value_a_shape.domain() == loco::Domain::Tensor);
+
+ auto value_a_tensor_shape = value_a_shape.as<loco::TensorShape>();
+ node_rank = value_a_tensor_shape.rank();
+ }
+
+ int32_t axis_value = 0;
+ {
+ // axis should be TFConst
+ auto axis_node = node->axis();
+ auto tfconst = dynamic_cast<moco::TFConst *>(axis_node);
+ if (tfconst == nullptr)
+ {
+ // TODO Check this: this error can be from TFOptimizatier.
+ throw oops::UserExn("ConcatV2 node has invalid input for axis", node->name());
+ }
+ auto result = scalar_value(tfconst, axis_value);
+ if (!result)
+ {
+ // TODO Check this: this error can be from TFOptimizatier.
+ throw oops::UserExn("ConcatV2 node has invalid input for axis", node->name());
+ }
+ }
+ uint32_t axis_absolute = (axis_value >= 0) ? axis_value : (int32_t)node_rank + axis_value;
+
+ INFO(l) << "canonicalize_concat axis(" << axis_absolute << "), value(" << axis_value << "), rank("
+ << node_rank << ")";
+
+ // Convert series of TensorConcat if num_values > 2
+ auto concat_node = graph->nodes()->create<loco::TensorConcat>();
+ concat_node->lhs(node->values(0));
+ concat_node->rhs(node->values(1));
+ concat_node->axis(axis_absolute);
+
+ loco::TensorConcat *last_concat = concat_node;
+ for (int ni = 2; ni < num_values; ++ni)
+ {
+ auto concat_node_next = graph->nodes()->create<loco::TensorConcat>();
+
+ concat_node_next->lhs(last_concat);
+ concat_node_next->rhs(node->values(ni));
+ concat_node_next->axis(axis_absolute);
+
+ // update last concat node
+ last_concat = concat_node_next;
+ }
+
+ // replace node
+ replace(node).with(last_concat);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool ConcatV2Canonicalizer::transform(TFConcatV2 *node) const
+{
+ return canonicalize_concat(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h
new file mode 100644
index 000000000..e6b471b89
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ConcatV2Canonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_CONCATV2_CANONICALIZER_H__
+#define __MOCO_TF_CONCATV2_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFConcatV2 to Canonical TensorConcat
+ */
+class ConcatV2Canonicalizer : public SimpleNodeTransform<moco::TFConcatV2>
+{
+public:
+ const char *name(void) const final { return "ConcatV2Canonicalizer"; }
+
+public:
+ bool transform(moco::TFConcatV2 *node) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_CONCATV2_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp
new file mode 100644
index 000000000..60629cd5a
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2019 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 "ConstCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Names.h>
+#include <moco/Log.h>
+
+#include <oops/UserExn.h>
+
+namespace
+{
+
+bool canonicalize_const(loco::Graph *graph, moco::TFConst *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFConst node with Canonical Const
+ *
+ * Before
+ * TFConst -- C
+ *
+ * After
+ * TFConst -
+ * ConstGen -- C
+ *
+ * Where
+ * C : a node that uses TFConst as an input
+ * TFConst is disconnected from other nodes
+ */
+
+ INFO(l) << "TFNodeCanonicalize TFConst begin";
+
+ auto const_node = graph->nodes()->create<loco::ConstGen>();
+
+ // copy properties
+ auto dtype = node->dtype();
+ const_node->dtype(dtype);
+
+ auto rank = node->rank();
+
+ if (rank == 0)
+ {
+ // This routine implements a workaround that converts a scalar constant (rank-0 tensor)
+ // into a rank-1 tensor of shape [1].
+ //
+ // TODO Revise this implementation later
+ const_node->rank(1);
+ const_node->dim(0) = 1;
+ }
+ else
+ {
+ const_node->rank(rank);
+
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (node->dim(r).known())
+ const_node->dim(r) = node->dim(r);
+ else
+ const_node->dim(r).unset();
+ }
+ }
+
+ switch (dtype)
+ {
+ case loco::DataType::S32:
+ {
+ uint32_t input_elements = node->size<loco::DataType::S32>();
+ const_node->size<loco::DataType::S32>(input_elements);
+ for (uint32_t i = 0; i < input_elements; ++i)
+ {
+ const_node->at<loco::DataType::S32>(i) = node->at<loco::DataType::S32>(i);
+ }
+ break;
+ }
+ case loco::DataType::FLOAT32:
+ {
+ uint32_t input_elements = node->size<loco::DataType::FLOAT32>();
+ const_node->size<loco::DataType::FLOAT32>(input_elements);
+ for (uint32_t i = 0; i < input_elements; ++i)
+ {
+ const_node->at<loco::DataType::FLOAT32>(i) = node->at<loco::DataType::FLOAT32>(i);
+ }
+ break;
+ }
+ default:
+ throw oops::UserExn("Const has unsupported data type", node->name());
+ }
+
+ // update graph
+ replace(node).with(const_node);
+
+ INFO(l) << "TFNodeCanonicalize TFConst done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool ConstCanonicalizer::transform(TFConst *node) const
+{
+ return canonicalize_const(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h
new file mode 100644
index 000000000..1b0b2b867
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ConstCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_CONST_CANONICALIZER_H__
+#define __MOCO_TF_CONST_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFConst to Canonical ConstGen
+ */
+class ConstCanonicalizer : public SimpleNodeTransform<moco::TFConst>
+{
+public:
+ const char *name(void) const final { return "ConstCanonicalizer"; }
+
+public:
+ bool transform(moco::TFConst *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_CONST_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp
new file mode 100644
index 000000000..d3cbd4ab3
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.cpp
@@ -0,0 +1,371 @@
+/*
+ * Copyright (c) 2019 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 "Conv2DBackpropInputCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "CodecHelper.h"
+
+#include <loco/IR/Stride.h>
+#include <loco/IR/Padding2D.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/UserExn.h>
+
+namespace
+{
+using plier::tf::DataLayout;
+
+void set_filter_enc(loco::FilterEncode *filter_enc)
+{
+ auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
+
+ // In TensorFlow, Conv2dBackpropInput's filter is a 4-D tensor of following shape:
+ // [filter_height, filter_width, out_channels, in_channels] or HWOI or HWNC (in/out in loco sense)
+ enc->perm()->axis(loco::FilterAxis::Height) = 0;
+ enc->perm()->axis(loco::FilterAxis::Width) = 1;
+ enc->perm()->axis(loco::FilterAxis::Count) = 2;
+ enc->perm()->axis(loco::FilterAxis::Depth) = 3;
+
+ filter_enc->encoder(std::move(enc));
+}
+
+} // namespace
+
+namespace
+{
+
+bool stride_2d_from_4d(loco::Stride<2> &ret, const std::vector<int64_t> &strides_4d,
+ const DataLayout data_layout)
+{
+ if (!(strides_4d.size() == 4))
+ return false;
+
+ switch (data_layout)
+ {
+ case DataLayout::NHWC:
+ ret.vertical(strides_4d.at(1));
+ ret.horizontal(strides_4d.at(2));
+ break;
+ case DataLayout::NCHW:
+ ret.vertical(strides_4d.at(2));
+ ret.horizontal(strides_4d.at(3));
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+struct PlaneShape
+{
+ loco::Dimension vertical;
+ loco::Dimension horizontal;
+};
+
+class Padding2DInference final
+{
+public:
+ Padding2DInference(const moco::TFNode *node) { _node = node; }
+
+public:
+ loco::Padding2D operator()(void);
+
+public:
+ PlaneShape &input() { return _input; }
+ PlaneShape &output() { return _output; }
+ loco::Stride<2> &stride() { return _stride; }
+ loco::Window<2> &window() { return _window; }
+ moco::TFPadding &padding() { return _padding; }
+
+private:
+ /// @brief Check whether ingredients set by non-default values
+ bool ready()
+ {
+ if (not input().vertical.known())
+ return false;
+ if (not input().horizontal.known())
+ return false;
+ if (not output().vertical.known())
+ return false;
+ if (not output().horizontal.known())
+ return false;
+ if (stride().vertical() == 0)
+ return false;
+ if (stride().horizontal() == 0)
+ return false;
+ if (window().vertical() == 0)
+ return false;
+ if (window().horizontal() == 0)
+ return false;
+ if (padding().empty())
+ return false;
+
+ return true;
+ }
+
+ inline uint32_t tight_output_for_valid_padding(uint32_t input, uint32_t stride, uint32_t filter)
+ {
+ return stride * (input - 1) + filter;
+ }
+
+ /**
+ * @note For Conv2DBackpropInput SAME padding, TensorFlow requires this condition to hold
+ *
+ * Reference: `::tensorflow::GetWindowedOutputSizeVerboseV2()` from TensorFlow project
+ */
+ inline bool same_padding_applicable(uint32_t input, uint32_t output, uint32_t stride)
+ {
+ // Here 'input' and 'output' means Conv2DBackpropInput's actual node input and output.
+ // Then these three conditions are equivalent:
+ //
+ // input == floor((output + stride - 1) / stride)
+ // input == ceil(output / stride)
+ // (stride * (input - 1) < output) and (output <= stride * input)
+ return (stride * (input - 1) < output) and (output <= stride * input);
+ }
+
+ inline uint32_t padding_needed(uint32_t input, uint32_t output, uint32_t stride, uint32_t filter)
+ {
+ return stride * (input - 1) + filter - output;
+ }
+
+private:
+ const moco::TFNode *_node;
+ PlaneShape _input;
+ PlaneShape _output;
+ loco::Stride<2> _stride;
+ loco::Window<2> _window;
+ moco::TFPadding _padding;
+};
+
+loco::Padding2D Padding2DInference::operator()(void)
+{
+ assert(ready());
+
+ if (padding() == "VALID")
+ {
+ // In case of VALID padding, TensorFlow accepts any size same or larger than
+ // 'tight fit' output. When output size (set by 'input sizes' node input) is
+ // larger than tight fit, extra spaces filled with zero.
+ auto tight_output_vertical = tight_output_for_valid_padding(
+ input().vertical.value(), stride().vertical(), window().vertical());
+ auto tight_output_horizontal = tight_output_for_valid_padding(
+ input().horizontal.value(), stride().horizontal(), window().horizontal());
+
+ if (output().vertical.value() < tight_output_vertical or
+ output().horizontal.value() < tight_output_horizontal)
+ throw oops::UserExn("input_sizes is too small", _node->name());
+
+ // Currently, only accept tight fit.
+ // TODO Support non-tight case by adding zero padding operation
+ assert(output().vertical.value() == tight_output_vertical);
+ assert(output().horizontal.value() == tight_output_horizontal);
+
+ return loco::Padding2D(0, 0, 0, 0);
+ }
+
+ if (padding() == "SAME")
+ {
+ // This condition is required by TensorFlow
+ if (not same_padding_applicable(input().vertical.value(), output().vertical.value(),
+ stride().vertical()) or
+ not same_padding_applicable(input().horizontal.value(), output().horizontal.value(),
+ stride().horizontal()))
+ throw oops::UserExn("Size mismatch for SAME padding", _node->name());
+
+ auto whole_pad_vertical = padding_needed(input().vertical.value(), output().vertical.value(),
+ stride().vertical(), window().vertical());
+ auto whole_pad_horizontal =
+ padding_needed(input().horizontal.value(), output().horizontal.value(),
+ stride().horizontal(), window().horizontal());
+
+ loco::Padding2D res;
+
+ res.top(whole_pad_vertical / 2);
+ res.bottom(whole_pad_vertical - res.top());
+ res.left(whole_pad_horizontal / 2);
+ res.right(whole_pad_horizontal - res.left());
+
+ return res;
+ }
+
+ throw oops::UserExn("Usupported padding " + padding(), _node->name());
+}
+
+/**
+ * @param[out] ret PlaneShape extracted from 'node' with given 'data_layout'
+ * @param[in] node
+ * @param[in] data_layout
+ *
+ * @return true on success
+ */
+bool set_plane_shape(PlaneShape &ret, const loco::Node *node, const DataLayout data_layout)
+{
+ auto tensor_shape = loco::shape_get(node).as<loco::TensorShape>();
+ if (!(tensor_shape.rank() == 4))
+ return false;
+
+ switch (data_layout)
+ {
+ case DataLayout::NHWC:
+ ret.vertical = tensor_shape.dim(1).value();
+ ret.horizontal = tensor_shape.dim(2).value();
+ break;
+ case DataLayout::NCHW:
+ ret.vertical = tensor_shape.dim(2).value();
+ ret.horizontal = tensor_shape.dim(3).value();
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @param[out] ret 2D Window extracted from HW** filter node
+ * @param[in] filter_node
+ *
+ * @return true on success
+ */
+bool set_window(loco::Window<2> &ret, const loco::Node *filter_node)
+{
+ auto tensor_shape = loco::shape_get(filter_node).as<loco::TensorShape>();
+ assert(tensor_shape.rank() == 4);
+
+ ret.vertical(tensor_shape.dim(0).value());
+ ret.horizontal(tensor_shape.dim(1).value());
+
+ return true;
+}
+
+} // namespace
+
+namespace
+{
+
+bool canonicalize_conv2d_backprop_input(loco::Graph *graph,
+ moco::TFConv2DBackpropInput *conv2d_backprop)
+{
+ /**
+ * @note This will replace TFConv2DBackpropInput node with canonical
+ * FeatureEncode + FilterEncode + TransposedConv2D + FeatureDecode
+ *
+ * Before
+ * input_sizes ----
+ * \
+ * filter -------- TFConv2DBackpropInput --- output(s)
+ * /
+ * out_backprop ---
+ *
+ * After
+ * input_sizes ----
+ * \
+ * filter -------- TFConv2DBackpropInput ---
+ * /
+ * out_backprop ---
+ *
+ * filter ------ FilterEncode ------ TransposedConv2D --- FeatureDecode --- output(s)
+ * (as ker) /
+ * out_backprop --- FeatureEncode ---
+ * (as ifm)
+ */
+
+ if (!loco::shape_known(conv2d_backprop->out_backprop()))
+ return false;
+ if (!loco::shape_known(conv2d_backprop))
+ return false;
+ if (!loco::shape_known(conv2d_backprop->filter()))
+ return false;
+
+ auto data_layout = plier::tf::as_data_layout(conv2d_backprop->data_layout());
+
+ // Nodes to replace
+ auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+ auto filter_enc = graph->nodes()->create<loco::FilterEncode>();
+ auto tr_conv2d = graph->nodes()->create<loco::TransposedConv2D>();
+ auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+ set_feature_enc(feature_enc, data_layout);
+ set_filter_enc(filter_enc);
+ set_feature_dec(feature_dec, data_layout);
+
+ // Attributes for new TransposedConv2D
+ loco::Stride<2> stride;
+ loco::Padding2D pad;
+
+ // Get attributes
+ {
+ if (!stride_2d_from_4d(stride, conv2d_backprop->strides(), data_layout))
+ throw oops::UserExn("Unsupported strides", conv2d_backprop->name());
+
+ Padding2DInference infer_pad(conv2d_backprop);
+
+ if (!set_plane_shape(infer_pad.input(), conv2d_backprop->out_backprop(), data_layout))
+ throw oops::UserExn("Unsupported out_backprop data_format", conv2d_backprop->name());
+ if (!set_plane_shape(infer_pad.output(), conv2d_backprop, data_layout))
+ throw oops::UserExn("Unsupported data_format", conv2d_backprop->name());
+ if (!set_window(infer_pad.window(), conv2d_backprop->filter()))
+ throw oops::UserExn("Unsupported filter shape", conv2d_backprop->name());
+ infer_pad.stride() = stride;
+ infer_pad.padding() = conv2d_backprop->padding();
+
+ // Run padding infer_pad
+ pad = infer_pad();
+ }
+
+ // Set attributes
+ tr_conv2d->pad()->top(pad.top());
+ tr_conv2d->pad()->bottom(pad.bottom());
+ tr_conv2d->pad()->left(pad.left());
+ tr_conv2d->pad()->right(pad.right());
+
+ tr_conv2d->stride()->vertical(stride.vertical());
+ tr_conv2d->stride()->horizontal(stride.horizontal());
+
+ // Update graph
+ auto input_node = conv2d_backprop->out_backprop();
+ auto filter_node = conv2d_backprop->filter();
+
+ // Update connections
+ feature_enc->input(input_node);
+ filter_enc->input(filter_node);
+ tr_conv2d->ifm(feature_enc);
+ tr_conv2d->ker(filter_enc);
+ feature_dec->input(tr_conv2d);
+
+ // Replace old conv2d_backprop
+ replace(conv2d_backprop).with(feature_dec);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool Conv2DBackpropInputCanonicalizer::transform(TFConv2DBackpropInput *node) const
+{
+ return canonicalize_conv2d_backprop_input(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h
new file mode 100644
index 000000000..bc37bb9cb
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DBackpropInputCanonicalizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__
+#define __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/// @brief Convert TFConv2DBackpropInput to Canonical TransposedConv2D
+class Conv2DBackpropInputCanonicalizer : public SimpleNodeTransform<moco::TFConv2DBackpropInput>
+{
+public:
+ const char *name(void) const final { return "Conv2DBackpropInputCanonicalizer"; }
+
+public:
+ bool transform(moco::TFConv2DBackpropInput *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_CONV2DBACKPROPINPUT_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp
new file mode 100644
index 000000000..a955793a8
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.cpp
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2019 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 "Conv2DCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include "CodecHelper.h"
+
+#include <moco/Log.h>
+
+namespace
+{
+using plier::tf::DataLayout;
+
+void set_filter_enc(loco::FilterEncode *filter_enc)
+{
+ auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
+
+ // In TensorFlow, conv2d filter is a 4-D tensor of following shape:
+ // [filter_height, filter_width, in_channels, out_channels] -> HWIO (HWCN)
+ enc->perm()->axis(loco::FilterAxis::Height) = 0;
+ enc->perm()->axis(loco::FilterAxis::Width) = 1;
+ enc->perm()->axis(loco::FilterAxis::Depth) = 2;
+ enc->perm()->axis(loco::FilterAxis::Count) = 3;
+
+ filter_enc->encoder(std::move(enc));
+}
+
+bool canonicalize_conv2d(loco::Graph *graph, moco::TFConv2D *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFCon2D node with Canonical FeatureEncode +
+ * FilterEncode + Conv2D + FeatureDecode
+ *
+ * Before
+ * A -- TFConv2D - C
+ * B -/
+ *
+ * After
+ * A -- TFConv2D -
+ * B -/
+ * A -- FeatureEncode - Conv2D - FeatureDecode - C
+ * B -- FilterEncode -/
+ *
+ * Where
+ * A : ifm of TFConv2D
+ * B : ker of TFConv2D
+ * C : a node that uses TFConv2D as an input
+ * TFConv2D is disconnected from other nodes
+ * A and B are drawn twice to simplify the diagram
+ */
+
+ auto data_layout = plier::tf::as_data_layout(node->data_layout());
+
+ auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+ auto filter_enc = graph->nodes()->create<loco::FilterEncode>();
+ auto conv2d = graph->nodes()->create<loco::Conv2D>();
+ auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+ set_feature_enc(feature_enc, data_layout);
+ set_filter_enc(filter_enc);
+ set_feature_dec(feature_dec, data_layout);
+
+ auto input_shape = moco::node_shape(node->input());
+ assert(input_shape.domain() != loco::Domain::Unknown);
+
+ auto ker_shape = moco::node_shape(node->filter());
+ auto ker_tensor_shape = ker_shape.as<loco::TensorShape>(); // in HWIO
+
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(ker_tensor_shape, "HWIO");
+
+ moco::Padding2DInference infer_padding2d;
+
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
+
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+
+ *conv2d->pad() = infer_padding2d(input_plane_shape);
+ *conv2d->stride() = node_stride;
+
+ // update graph
+ auto node_A = node->input();
+ auto node_B = node->filter();
+
+ // update connections
+ feature_enc->input(node_A);
+ filter_enc->input(node_B);
+ conv2d->ifm(feature_enc);
+ conv2d->ker(filter_enc);
+ feature_dec->input(conv2d);
+
+ // replace old node
+ replace(node).with(feature_dec);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool Conv2DCanonicalizer::transform(TFConv2D *node) const
+{
+ return canonicalize_conv2d(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h
new file mode 100644
index 000000000..ea39667f3
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Conv2DCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_CONV2D_CANONICALIZER_H__
+#define __MOCO_TF_CONV2D_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFConv2D to Canonical Conv2D
+ */
+class Conv2DCanonicalizer : public SimpleNodeTransform<TFConv2D>
+{
+public:
+ const char *name(void) const final { return "Conv2DCanonicalizer"; }
+
+public:
+ bool transform(TFConv2D *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_CONV2D_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp
new file mode 100644
index 000000000..50dddf637
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2019 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 "DepthwiseConv2dNativeCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include "CodecHelper.h"
+
+#include <moco/Log.h>
+
+namespace
+{
+
+using plier::tf::DataLayout;
+
+void set_filter_enc(loco::DepthwiseFilterEncode *filter_enc)
+{
+ auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::DepthwiseFilter>>();
+
+ // In TensorFlow, depthwiseconv2dnative filter is a 4-D tensor of following shape:
+ // [filter_height, filter_width, in_channels, channel_multiplier] -> HWCM
+ enc->perm()->axis(loco::DepthwiseFilterAxis::Height) = 0;
+ enc->perm()->axis(loco::DepthwiseFilterAxis::Width) = 1;
+ enc->perm()->axis(loco::DepthwiseFilterAxis::Depth) = 2;
+ enc->perm()->axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+
+ filter_enc->encoder(std::move(enc));
+}
+
+bool canonicalize_depthwiseconv2dnative(loco::Graph *graph, moco::TFDepthwiseConv2dNative *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFDepthwiseConv2dNative node with Canonical FeatureEncode +
+ * DepthwiseFilterEncode + DepthwiseConv2D + FeatureDecode
+ *
+ * Before
+ * A -+- TFDepthwiseConv2dNative - C
+ * |
+ * B -+
+ *
+ * After
+ *
+ * A -+ FeatureEncode ----------------+- DepthwiseConv2D - FeatureDecode - C
+ * | |
+ * +-(TFDepthwiseConv2dNative) |
+ * | |
+ * B -+ DepthwiseFilterEncode --------+
+ *
+ * Where
+ * A : ifm of TFDepthwiseConv2dNative
+ * B : ker of TFDepthwiseConv2dNative
+ * C : a node that uses TFDepthwiseConv2dNative as an input
+ * TFDepthwiseConv2dNative is disconnected from other nodes
+ */
+
+ INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative begin";
+
+ auto data_layout = plier::tf::as_data_layout(node->data_layout());
+
+ auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+ auto filter_enc = graph->nodes()->create<loco::DepthwiseFilterEncode>();
+ auto depthwiseconv2d = graph->nodes()->create<loco::DepthwiseConv2D>();
+ auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+ set_feature_enc(feature_enc, data_layout);
+ set_filter_enc(filter_enc);
+ set_feature_dec(feature_dec, data_layout);
+
+ // Calculate Pad and Stride from inference
+ auto input_shape = moco::node_shape(node->input());
+ auto ker_shape = moco::node_shape(node->filter());
+ auto ker_tensor_shape = ker_shape.as<loco::TensorShape>();
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(ker_tensor_shape, "HWCM");
+
+ moco::Padding2DInference infer_padding2d;
+
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
+
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+
+ *depthwiseconv2d->pad() = infer_padding2d(input_plane_shape);
+ *depthwiseconv2d->stride() = node_stride;
+
+ // update graph
+ auto node_A = node->input();
+ auto node_B = node->filter();
+
+ // update connections
+ feature_enc->input(node_A);
+ filter_enc->input(node_B);
+ depthwiseconv2d->ifm(feature_enc);
+ depthwiseconv2d->ker(filter_enc);
+ feature_dec->input(depthwiseconv2d);
+
+ // replace and disconnect old node
+ replace(node).with(feature_dec);
+
+ INFO(l) << "TFNodeCanonicalize TFDepthwiseConv2dNative done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool DepthwiseConv2dNativeCanonicalizer::transform(TFDepthwiseConv2dNative *node) const
+{
+ return canonicalize_depthwiseconv2dnative(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h
new file mode 100644
index 000000000..704e1ade9
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/DepthwiseConv2dNativeCanonicalizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
+#define __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFDepthwiseConv2dNative to Canonical DepthwiseConv2D
+ */
+class DepthwiseConv2dNativeCanonicalizer : public SimpleNodeTransform<moco::TFDepthwiseConv2dNative>
+{
+public:
+ const char *name(void) const final { return "DepthwiseConv2dNativeCanonicalizer"; }
+
+public:
+ bool transform(moco::TFDepthwiseConv2dNative *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_DEPTHWISE_CONV2D_NATIVE_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp
new file mode 100644
index 000000000..3b680cf04
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 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 "IdentityCanonicalizer.h"
+
+#include "Convert.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Names.h>
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_identity(loco::Graph *graph, moco::TFIdentity *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFIdentity node with Canonical Forward
+ *
+ * Before
+ * A -- TFIdentity -- C
+ *
+ * After
+ * /- TFIdentity --
+ * A -- Forward -- C
+ *
+ * Where
+ * A : input of TFIdentity
+ * C : a node that uses TFIdentity as an input
+ * TFIdentity is disconnected from the output
+ */
+
+ INFO(l) << "TFNodeCanonicalize TFIdentity begin";
+
+ auto forward_node = graph->nodes()->create<loco::Forward>();
+
+ auto node_A = node->input();
+
+ forward_node->input(node_A);
+
+ // update graph
+ replace(node).with(forward_node);
+
+ INFO(l) << "TFNodeCanonicalize TFIdentity done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool IdentityCanonicalizer::transform(TFIdentity *node) const
+{
+ return canonicalize_identity(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h
new file mode 100644
index 000000000..59b2894c5
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/IdentityCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_IDENTITY_CANONICALIZER_H__
+#define __MOCO_TF_IDENTITY_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFIdentity to Canonical Forward
+ */
+class IdentityCanonicalizer : public SimpleNodeTransform<moco::TFIdentity>
+{
+public:
+ const char *name(void) const final { return "IdentityCanonicalizer"; }
+
+public:
+ bool transform(moco::TFIdentity *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_IDENTITY_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp
new file mode 100644
index 000000000..06a605717
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2019 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 "MaxPoolCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include "CodecHelper.h"
+
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_maxpool2d(loco::Graph *graph, moco::TFMaxPool *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFMaxPool node with Canonical FeatureEncode +
+ * MaxPool2D + FeatureDecode
+ *
+ * Before
+ * A -- TFMaxPool -- C
+ *
+ * After
+ * +- TFMaxPool --
+ * |
+ * A -+- FeatureEncode -- MaxPool2D -- FeatureDecode -- C
+ *
+ * Where
+ * A : value of TFMaxPool
+ * C : a node that uses TFMaxPool as an input
+ * TFMaxPool is disconnected from other nodes
+ */
+
+ auto data_layout = plier::tf::as_data_layout(node->data_layout());
+
+ auto feature_enc = graph->nodes()->create<loco::FeatureEncode>();
+ auto maxPool2d_node = graph->nodes()->create<loco::MaxPool2D>();
+ auto feature_dec = graph->nodes()->create<loco::FeatureDecode>();
+
+ set_feature_enc(feature_enc, data_layout);
+ set_feature_dec(feature_dec, data_layout);
+
+ // paddata to pad
+ auto input_shape = moco::node_shape(node->input());
+ assert(input_shape.domain() != loco::Domain::Unknown);
+
+ auto node_stride = moco::stride_of(node->strides(), node->data_layout());
+ auto node_window = moco::window_of(node->ksize(), node->data_layout());
+
+ moco::Padding2DInference infer_padding2d;
+
+ infer_padding2d.padding(node->padding());
+ infer_padding2d.stride(node_stride);
+ infer_padding2d.window(node_window);
+
+ auto input_feature_shape = moco::as_feature_shape(input_shape, node->data_layout());
+ auto input_plane_shape = moco::make_plane_shape(input_feature_shape);
+
+ *maxPool2d_node->pad() = infer_padding2d(input_plane_shape);
+ *maxPool2d_node->stride() = node_stride;
+ *maxPool2d_node->window() = node_window;
+
+ INFO(l) << "Canonicalize TFMaxPool pad = T " << maxPool2d_node->pad()->top() << ", L "
+ << maxPool2d_node->pad()->left() << ", B " << maxPool2d_node->pad()->bottom() << ", R "
+ << maxPool2d_node->pad()->right() << std::endl;
+
+ // update graph
+ auto node_A = node->input();
+
+ // update connections
+ feature_enc->input(node_A);
+ maxPool2d_node->ifm(feature_enc);
+ feature_dec->input(maxPool2d_node);
+
+ // replace node
+ replace(node).with(feature_dec);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool MaxPoolCanonicalizer::transform(TFMaxPool *node) const
+{
+ return canonicalize_maxpool2d(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h
new file mode 100644
index 000000000..c58ade528
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MaxPoolCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_MAXPOOL_CANONICALIZER_H__
+#define __MOCO_TF_MAXPOOL_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFMaxPool to Canonical MaxPool2D
+ */
+class MaxPoolCanonicalizer : public SimpleNodeTransform<moco::TFMaxPool>
+{
+public:
+ const char *name(void) const final { return "MaxPoolCanonicalizer"; }
+
+public:
+ bool transform(moco::TFMaxPool *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_MAXPOOL_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp
new file mode 100644
index 000000000..92634d01f
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 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 "MaximumCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "TFEltwiseBinaryCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool MaximumCanonicalizer::transform(moco::TFMaximum *node) const
+{
+ return canonicalize_eltwise_binary_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h
new file mode 100644
index 000000000..baff4d7ad
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MaximumCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020 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.
+ */
+
+#ifndef __MOCO_TF_MAXIMUM_CANONICALIZER_H__
+#define __MOCO_TF_MAXIMUM_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFMaximum to Canonical EltwiseMax
+ */
+class MaximumCanonicalizer : public SimpleNodeTransform<moco::TFMaximum>
+{
+public:
+ const char *name(void) const final { return "MaximumCanonicalizer"; }
+
+public:
+ bool transform(moco::TFMaximum *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_MAXIMUM_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp
new file mode 100644
index 000000000..69eaf7900
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 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 "MeanCanonicalizer.h"
+#include "TFReduceCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool MeanCanonicalizer::transform(moco::TFMean *node) const
+{
+ return canonicalize_reduce_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h
new file mode 100644
index 000000000..469d7e3cd
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MeanCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_MEAN_CANONICALIZER_H__
+#define __MOCO_TF_MEAN_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Canonicalize TF-dialect TFMean into canonical TensorReduce(Mean) node
+ */
+class MeanCanonicalizer : public SimpleNodeTransform<moco::TFMean>
+{
+public:
+ const char *name(void) const final { return "MeanCanonicalizer"; }
+
+public:
+ bool transform(moco::TFMean *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_MEAN_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp
new file mode 100644
index 000000000..d02f71361
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 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 "MulCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "TFEltwiseBinaryCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool MulCanonicalizer::transform(moco::TFMul *node) const
+{
+ return canonicalize_eltwise_binary_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h
new file mode 100644
index 000000000..480eec700
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/MulCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_MUL_CANONICALIZER_H__
+#define __MOCO_TF_MUL_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFMul to Canonical EltwiseMul
+ */
+class MulCanonicalizer : public SimpleNodeTransform<moco::TFMul>
+{
+public:
+ const char *name(void) const final { return "MulCanonicalizer"; }
+
+public:
+ bool transform(moco::TFMul *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_MUL_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp
new file mode 100644
index 000000000..10816f47c
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2019 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 "PadCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "loco/Service/TypeInference.h"
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_pad(loco::Graph *graph, moco::TFPad *node)
+{
+ /**
+ * @note This will replace TFPad node with Canonical TensorConstantPad
+ *
+ * Before
+ * input --- TFPad -- C
+ * paddings --/
+ * After
+ * paddings ------- TFPad --
+ * /
+ * input ----------- TensorConstantPad -- C
+ * ConstGen --------/
+ * Where
+ * input : input of TFPad
+ * paddings : paddings of TFPad. it becomes TensorConstantPad's attribute.
+ * C : a node that uses TFPad as an input. TFPad is disconnected from C.
+ * ConstGen : constant value of Pad. TFPad has zero value by default.
+ */
+
+ auto pad_node = graph->nodes()->create<loco::TensorConstantPad>();
+
+ auto constant_node = graph->nodes()->create<loco::ConstGen>();
+
+ auto input_node = node->input();
+ // TODO: support other dtype.
+ assert(loco::dtype_get(input_node) == loco::DataType::FLOAT32);
+ constant_node->dtype(loco::DataType::FLOAT32);
+ // TODO: constant node changes to scalar when it is implemented.
+ constant_node->shape({1});
+ constant_node->size<loco::DataType::FLOAT32>(1);
+ constant_node->at<loco::DataType::FLOAT32>(0) = 0.0f;
+
+ auto const_paddings_node = dynamic_cast<loco::ConstGen *>(node->paddings());
+ // TODO: support S64 type.
+ assert(const_paddings_node->dtype() == loco::DataType::S32);
+ assert(const_paddings_node->rank() == 2);
+ assert(const_paddings_node->dim(1).value() == 2);
+
+ auto padding = pad_node->padding();
+ uint32_t padding_rank = const_paddings_node->dim(0).value();
+ padding->rank(padding_rank);
+
+ for (uint32_t i = 0; i < padding_rank; i++)
+ {
+ padding->front(i) = const_paddings_node->at<loco::DataType::S32>(i << 1);
+ padding->back(i) = const_paddings_node->at<loco::DataType::S32>((i << 1) + 1);
+ }
+
+ // update connections
+ pad_node->input(input_node);
+ pad_node->constant(constant_node);
+
+ // replace node
+ replace(node).with(pad_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool PadCanonicalizer::transform(TFPad *node) const
+{
+ return canonicalize_pad(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h
new file mode 100644
index 000000000..64bb6041a
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PadCanonicalizer.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_PAD_CANONICALIZER_H__
+#define __MOCO_TF_PAD_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFPad to Canonical TensorConstantPad
+ */
+class PadCanonicalizer final : public SimpleNodeTransform<moco::TFPad>
+{
+public:
+ const char *name(void) const final { return "PadCanonicalizer"; }
+
+public:
+ bool transform(moco::TFPad *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PAD_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp
new file mode 100644
index 000000000..f568e909f
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.cpp
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2019 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 "PlaceholderCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Names.h>
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_placeholder(loco::Graph *graph, moco::TFPlaceholder *node)
+{
+ LOGGER(l);
+
+ /**
+ * @note This will replace TFPlaceholder node with Canonical Pull
+ *
+ * Before
+ * TFPlaceholder -- C
+ *
+ * After
+ * TFPlaceholder -
+ * Pull -- C
+ *
+ * Where
+ * C : a node that uses TFPlaceholder as an input
+ * TFPlaceholder is disconnected from other nodes
+ */
+
+ INFO(l) << "PlaceholderCanonicalizer begin";
+
+ auto pull_node = graph->nodes()->create<loco::Pull>();
+
+ // copy properties
+ auto dtype = node->dtype();
+ pull_node->dtype(dtype);
+
+ auto rank = node->rank();
+
+ if (rank == 0)
+ {
+ // This routine implements a workaround that converts a scalar constant (rank-0 tensor)
+ // into a rank-1 tensor of shape [1].
+ //
+ // TODO Revise this implementation later
+ pull_node->rank(1);
+ pull_node->dim(0) = 1;
+ }
+ else
+ {
+ pull_node->rank(rank);
+
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (node->dim(r).known())
+ pull_node->dim(r) = node->dim(r);
+ else
+ pull_node->dim(r).unset();
+ }
+ }
+
+ // set loco::Pull GraphInputIndex
+ pull_node->index(moco::index(node));
+
+ // update graph
+ replace(node).with(pull_node);
+
+ INFO(l) << "PlaceholderCanonicalizer done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool PlaceholderCanonicalizer::transform(TFPlaceholder *node) const
+{
+ return canonicalize_placeholder(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h
new file mode 100644
index 000000000..66eafe6af
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/PlaceholderCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__
+#define __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/Nodes/TFPlaceholder.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFPlaceholder to Canonical Pull
+ *
+ * @note GraphInputIndex is copied to Pull
+ */
+class PlaceholderCanonicalizer : public SimpleNodeTransform<::moco::TFPlaceholder>
+{
+public:
+ const char *name(void) const final { return "PlaceholderCanonicalizer"; }
+
+public:
+ bool transform(moco::TFPlaceholder *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PLACEHOLDER_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp
new file mode 100644
index 000000000..a448d85fa
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 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 "RealDivCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "TFEltwiseBinaryCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool RealDivCanonicalizer::transform(moco::TFRealDiv *node) const
+{
+ return canonicalize_eltwise_binary_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h
new file mode 100644
index 000000000..76e1bd377
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/RealDivCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_REALDIV_CANONICALIZER_H__
+#define __MOCO_TF_REALDIV_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFRealDiv to Canonical EltwiseDiv
+ */
+class RealDivCanonicalizer : public SimpleNodeTransform<moco::TFRealDiv>
+{
+public:
+ const char *name(void) const final { return "RealDivCanonicalizer"; }
+
+public:
+ bool transform(moco::TFRealDiv *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_REALDIV_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp
new file mode 100644
index 000000000..c53a880a8
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 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 "Relu6Canonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_relu6(loco::Graph *graph, moco::TFRelu6 *node)
+{
+ /**
+ * @note This will replace TFRelu6 node with Canonical ReLU6
+ *
+ * Before
+ * A --- TFRelu6 -- C
+ * After
+ * +- TFRelu6 --
+ * |
+ * A -+- ReLU6 -- C
+ *
+ * Where
+ * A : features of TFRelu6
+ * C : a node that uses TFRelu6 as an input
+ * TFRelu6 is disconnected from C
+ */
+
+ auto relu6_node = graph->nodes()->create<loco::ReLU6>();
+
+ auto node_A = node->features();
+
+ // update connections
+ relu6_node->input(node_A);
+
+ // replace node
+ replace(node).with(relu6_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool Relu6Canonicalizer::transform(TFRelu6 *node) const
+{
+ return canonicalize_relu6(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h
new file mode 100644
index 000000000..d8ad5db8e
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/Relu6Canonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_RELU6_CANONICALIZER_H__
+#define __MOCO_TF_RELU6_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFRelu6 to Canonical ReLU6
+ */
+class Relu6Canonicalizer : public SimpleNodeTransform<moco::TFRelu6>
+{
+public:
+ const char *name(void) const final { return "Relu6Canonicalizer"; }
+
+public:
+ bool transform(moco::TFRelu6 *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_RELU6_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp
new file mode 100644
index 000000000..7965dc931
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 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 "ReluCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_relu(loco::Graph *graph, moco::TFRelu *node)
+{
+ /**
+ * @note This will replace TFRelu node with Canonical ReLU
+ *
+ * Before
+ * A --- TFRelu -- C
+ * After
+ * +- TFRelu --
+ * |
+ * A -+- ReLU -- C
+ *
+ * Where
+ * A : features of TFRelu
+ * C : a node that uses TFRelu as an input
+ * TFRelu is disconnected from C
+ */
+
+ auto relu_node = graph->nodes()->create<loco::ReLU>();
+
+ auto node_A = node->features();
+
+ // update connections
+ relu_node->input(node_A);
+
+ // replace node
+ replace(node).with(relu_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool ReluCanonicalizer::transform(TFRelu *node) const
+{
+ return canonicalize_relu(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h
new file mode 100644
index 000000000..e27abe158
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ReluCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_RELU_CANONICALIZER_H__
+#define __MOCO_TF_RELU_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFRelu to Canonical ReLU
+ */
+class ReluCanonicalizer : public SimpleNodeTransform<moco::TFRelu>
+{
+public:
+ const char *name(void) const final { return "ReluCanonicalizer"; }
+
+public:
+ bool transform(moco::TFRelu *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_RELU_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp
new file mode 100644
index 000000000..b944568e0
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2019 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 "ReshapeCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Log.h>
+#include <plier/tf/Convert.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+
+namespace
+{
+using plier::tf::DataLayout;
+
+/**
+ * @brief Check whether given 'new shape' arg is a fixed shape input for Reshape
+ *
+ * ConstNode can be moco::TFConst or loco::ConstGen
+ */
+template <typename ConstNode> bool is_fixed_shape_input(ConstNode *const_shape_input)
+{
+ if (const_shape_input == nullptr)
+ return false;
+
+ // Shape input should be integer tensor of rank 1, e.g. [2, 3, 4] or [3, -1]
+ // TODO Support other possible data types, e.g. S64
+ assert(const_shape_input->dtype() == loco::DataType::S32);
+ assert(const_shape_input->rank() == 1);
+
+ auto shape_rank = const_shape_input->dim(0).value();
+ assert(shape_rank > 0);
+
+ for (uint32_t axis = 0; axis < shape_rank; ++axis)
+ {
+ auto shape_dim = const_shape_input->template at<loco::DataType::S32>(axis);
+ if (shape_dim == -1)
+ {
+ // has wildcard dimension, i.e. dynamic reshape
+ return false;
+ }
+ if (!(shape_dim >= 1))
+ {
+ throw oops::UserExn("New shape of Reshape has invalid dimension");
+ }
+ }
+ return true;
+}
+
+/// @note Currently only supports to canonicalize Fixed Reshape
+bool canonicalize_reshape(loco::Graph *graph, moco::TFReshape *node)
+{
+ LOGGER(l);
+ INFO(l) << "TFNodeCanonicalize TFReshape begin";
+
+ /**
+ * This rule canonicalizes TFReshape only when its output shape is known at
+ * compile time, i.e. fixed reshape case.
+ * TODO Support other cases like dynamic reshape
+ *
+ * This will replace TFReshape + TFConst or Canonical ConstGen(as shape input)
+ * node pair into Canonical Reshape<ReshapeType::Fixed>, or 'FixedReshape'.
+ * Shape input (TFConst or Canonical ConstGen) should not have wildcard
+ * dimension to be converted to FixedReshape.
+ *
+ * Before
+ * TFConst (shape)
+ * or ---
+ * ConstGen \
+ * \
+ * In --------- TFReshape ------- Out(s)
+ * (tensor)
+ *
+ * After
+ * TFConst
+ * or ---
+ * ConstGen \
+ * \
+ * ---------- TFReshape
+ * /
+ * In -------- FixedReshape ----- Out(s)
+ */
+
+ // create loco node to replace
+ auto fixed_reshape = graph->nodes()->create<loco::FixedReshape>();
+
+ // Supports 2 cases for Reshape's shape input:
+ // TF-dialect TFConst or Canonical ConstGen
+ loco::Node *shape_input = node->shape();
+ auto tfconst_shape_input = dynamic_cast<moco::TFConst *>(shape_input);
+ auto constgen_shape_input = dynamic_cast<loco::ConstGen *>(shape_input);
+
+ if (tfconst_shape_input)
+ {
+ // Only support fixed reshape
+ // TODO support dynamic reshape
+ if (!(is_fixed_shape_input(tfconst_shape_input)))
+ {
+ throw oops::UserExn("Supports only fixed reshape", node->name());
+ }
+
+ auto rank = tfconst_shape_input->dim(0).value();
+ fixed_reshape->rank(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ fixed_reshape->dim(axis) = tfconst_shape_input->at<loco::DataType::S32>(axis);
+ }
+ }
+ else if (constgen_shape_input)
+ {
+ // ditto
+ if (!(is_fixed_shape_input(constgen_shape_input)))
+ {
+ throw oops::UserExn("Supports only fixed reshape", node->name());
+ }
+
+ auto rank = constgen_shape_input->dim(0).value();
+ fixed_reshape->rank(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ fixed_reshape->dim(axis) = constgen_shape_input->at<loco::DataType::S32>(axis);
+ }
+ }
+ else
+ {
+ // TODO support dynamic reshape from not const node
+ throw oops::UserExn("Supports only const node as input shape", node->name());
+ }
+
+ // replace
+ auto in = node->tensor();
+ fixed_reshape->input(in);
+
+ replace(node).with(fixed_reshape);
+
+ INFO(l) << "TFNodeCanonicalize TFReshape done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool ReshapeCanonicalizer::transform(TFReshape *node) const
+{
+ return canonicalize_reshape(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h
new file mode 100644
index 000000000..1a792024e
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/ReshapeCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_RESHAPE_CANONICALIZER_H__
+#define __MOCO_TF_RESHAPE_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFReshape to Canonical Reshape
+ */
+class ReshapeCanonicalizer : public SimpleNodeTransform<moco::TFReshape>
+{
+public:
+ const char *name(void) const final { return "ReshapeCanonicalizer"; }
+
+public:
+ bool transform(moco::TFReshape *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_RESHAPE_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp
new file mode 100644
index 000000000..c31dbf6d6
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.cpp
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 2019 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 "RsqrtCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include <moco/Log.h>
+
+#include <loco/Service/TypeInference.h>
+
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+namespace
+{
+
+template <typename T>
+bool prepare_const_gen(loco::ConstGen *const_node, const loco::TensorShape &tensorshape, T value);
+
+template <>
+bool prepare_const_gen<float>(loco::ConstGen *const_node, const loco::TensorShape &tensorshape,
+ float value)
+{
+ LOGGER(l);
+
+ uint32_t const_num_elements = 1;
+
+ auto dtype = loco::DataType::FLOAT32;
+ const_node->dtype(dtype);
+
+ auto rank = tensorshape.rank();
+ const_node->rank(rank);
+ for (uint32_t r = 0; r < rank; ++r)
+ {
+ if (tensorshape.dim(r).known())
+ const_node->dim(r) = tensorshape.dim(r);
+ else
+ return false;
+
+ assert(tensorshape.dim(r).value() > 0);
+
+ const_num_elements *= tensorshape.dim(r).value();
+ }
+
+ INFO(l) << "prepare_const_gen : Elements = " << const_num_elements;
+
+ const_node->size<loco::DataType::FLOAT32>(const_num_elements);
+ for (uint32_t i = 0; i < const_num_elements; ++i)
+ {
+ const_node->at<loco::DataType::FLOAT32>(i) = value;
+ }
+
+ return true;
+}
+
+bool canonicalize_rsqrt(loco::Graph *graph, moco::TFRsqrt *node)
+{
+ /**
+ * @note This will replace TFRsqrt node with Canonical EltwiseSqrt + EltwiseRealDiv
+ *
+ * Before
+ * A --- TFRsqrt -- C
+ * After
+ * +- TFRsqrt --
+ * |
+ * | ConstGen --+
+ * | \
+ * A -+- EltwiseSqrt -- EltwiseDiv -- C
+ *
+ * Where
+ * A : features of TFRsqrt
+ * C : a node that uses TFSqrt as an input
+ * TFRsqrt is disconnected from C
+ * TFRsqrt is converted to 1 / EltwiseSqrt
+ */
+
+ auto nodeshape = moco::node_shape(node);
+ if (nodeshape.domain() == loco::Domain::Unknown)
+ {
+ // We need this shape information
+ assert(false); // this shouldn't happen, let's add an alarm
+ return false;
+ }
+ auto tensorshape = nodeshape.as<loco::TensorShape>();
+
+ if (!loco::dtype_known(node))
+ {
+ // We need type of this node
+ return false;
+ }
+
+ auto sqrt_node = graph->nodes()->create<loco::EltwiseSqrt>();
+ auto eltdiv_node = graph->nodes()->create<loco::EltwiseDiv>();
+ auto const_node = graph->nodes()->create<loco::ConstGen>();
+
+ auto dtype = loco::dtype_get(node);
+
+ switch (dtype)
+ {
+ case loco::DataType::FLOAT32:
+ if (!prepare_const_gen<float>(const_node, tensorshape, 1.0f))
+ throw oops::UserExn("Cannot handle unknown shape", node->name());
+ break;
+
+ default:
+ throw oops::UserExn("Unsupported data type", node->name());
+ }
+
+ auto node_A = node->x();
+
+ // update connections
+ sqrt_node->input(node_A);
+ eltdiv_node->lhs(const_node);
+ eltdiv_node->rhs(sqrt_node);
+
+ // replace node
+ replace(node).with(eltdiv_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool RsqrtCanonicalizer::transform(TFRsqrt *node) const
+{
+ return canonicalize_rsqrt(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h
new file mode 100644
index 000000000..7fd4ff697
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/RsqrtCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_RSQRT_CANONICALIZER_H__
+#define __MOCO_TF_RSQRT_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFRsqrt to Canonical EltwiseDiv + EltwiseSqrt
+ */
+class RsqrtCanonicalizer : public SimpleNodeTransform<moco::TFRsqrt>
+{
+public:
+ const char *name(void) const final { return "RsqrtCanonicalizer"; }
+
+public:
+ bool transform(moco::TFRsqrt *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_RSQRT_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp
new file mode 100644
index 000000000..98af7b693
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2019 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 "SoftmaxCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_softmax(loco::Graph *graph, moco::TFSoftmax *node)
+{
+ LOGGER(l);
+
+ INFO(l) << "TFNodeCanonicalize TFSoftmax begin";
+
+ /**
+ * This will replace shape inferred TFSoftmax node into canonical TensorSoftmax
+ *
+ * Before
+ * In ---- TFSoftmax ---- Out(s)
+ *
+ * After
+ * ------ TFSoftmax
+ * /
+ * In ---- TensorSoftmax ----- Out(s)
+ */
+
+ auto nodeshape = moco::node_shape(node);
+ // Canonicalization into TensorSoftmax is valid when softmax has shape info
+ assert(nodeshape.domain() != loco::Domain::Unknown);
+
+ auto softmax_tensor_shape = nodeshape.as<loco::TensorShape>();
+
+ // Create loco node to replace
+ auto softmax = graph->nodes()->create<loco::TensorSoftmax>();
+
+ // replace
+ auto in = node->logits();
+ softmax->input(in);
+ softmax->axis(softmax_tensor_shape.rank() - 1);
+ replace(node).with(softmax);
+
+ INFO(l) << "TFNodeCanonicalize TFSoftmax done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool SoftmaxCanonicalizer::transform(TFSoftmax *node) const
+{
+ return canonicalize_softmax(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h
new file mode 100644
index 000000000..ebaf04cfe
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SoftmaxCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_SOFTMAX_CANONICALIZER_H__
+#define __MOCO_TF_SOFTMAx_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+* @brief Canonicalize TF-dialect TFSoftmax into canonical Softmax node
+*/
+class SoftmaxCanonicalizer : public SimpleNodeTransform<moco::TFSoftmax>
+{
+public:
+ const char *name(void) const final { return "SoftmaxCanonicalizer"; }
+
+public:
+ bool transform(moco::TFSoftmax *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SOFTMAX_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp
new file mode 100644
index 000000000..89b9b8a44
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019 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 "SqrtCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+namespace
+{
+
+bool canonicalize_sqrt(loco::Graph *graph, moco::TFSqrt *node)
+{
+ /**
+ * @note This will replace TFSqrt node with Canonical EltwiseSqrt
+ *
+ * Before
+ * A --- TFSqrt -- C
+ * After
+ * +- TFSqrt --
+ * |
+ * A -+- EltwiseSqrt -- C
+ *
+ * Where
+ * A : features of TFSqrt
+ * C : a node that uses TFSqrt as an input
+ * TFSqrt is disconnected from C
+ */
+
+ auto sqrt_node = graph->nodes()->create<loco::EltwiseSqrt>();
+
+ auto node_A = node->x();
+
+ // update connections
+ sqrt_node->input(node_A);
+
+ // replace node
+ replace(node).with(sqrt_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool SqrtCanonicalizer::transform(TFSqrt *node) const
+{
+ return canonicalize_sqrt(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h
new file mode 100644
index 000000000..3f7ffead8
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SqrtCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_SQRT_CANONICALIZER_H__
+#define __MOCO_TF_SQRT_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFsqrt to Canonical EltwiseSqrt
+ */
+class SqrtCanonicalizer : public SimpleNodeTransform<moco::TFSqrt>
+{
+public:
+ const char *name(void) const final { return "SqrtCanonicalizer"; }
+
+public:
+ bool transform(moco::TFSqrt *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SQRT_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp
new file mode 100644
index 000000000..f5b991206
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2019 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 "SqueezeCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/Support/TFShapeInferenceHelper.h>
+
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_squeeze_to_reshape(loco::Graph *graph, moco::TFSqueeze *node)
+{
+ LOGGER(l);
+
+ INFO(l) << "TFNodeCanonicalize TFSqueeze begin";
+
+ /**
+ * This will replace shape inferred TFSqueeze node into canonical FixedReshape
+ *
+ * Before
+ * In ---- TFSqueeze ---- Out(s)
+ *
+ * After
+ * ------ TFSqueeze
+ * /
+ * In ---- FixedReshape ----- Out(s)
+ */
+
+ auto nodeshape = moco::node_shape(node);
+ // canonicalize into FixedReshape is valid when squeeze has shape info
+ // TODO Support general Squeeze case
+ assert(nodeshape.domain() != loco::Domain::Unknown);
+
+ auto squeeze_tensor_shape = nodeshape.as<loco::TensorShape>();
+
+ // Create loco node to replace
+ auto reshape = graph->nodes()->create<loco::FixedReshape>();
+
+ // Copy shape
+ reshape->rank(squeeze_tensor_shape.rank());
+ for (uint32_t axis = 0; axis < squeeze_tensor_shape.rank(); ++axis)
+ {
+ assert(squeeze_tensor_shape.dim(axis).known());
+ reshape->dim(axis) = squeeze_tensor_shape.dim(axis);
+ }
+
+ // replace
+ auto in = node->input();
+ reshape->input(in);
+ replace(node).with(reshape);
+
+ INFO(l) << "TFNodeCanonicalize TFSqueeze done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool SqueezeCanonicalizer::transform(TFSqueeze *node) const
+{
+ return canonicalize_squeeze_to_reshape(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h
new file mode 100644
index 000000000..28a1442bd
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SqueezeCanonicalizer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_SQUEEZE_CANONICALIZER_H__
+#define __MOCO_TF_SQUEEZE_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Canonicalize TF-dialect TFSqueeze into canonical FixedReshape node
+ *
+ * @note There is no canonical Squeeze node
+ */
+class SqueezeCanonicalizer : public SimpleNodeTransform<moco::TFSqueeze>
+{
+public:
+ const char *name(void) const final { return "SqueezeCanonicalizer"; }
+
+public:
+ bool transform(moco::TFSqueeze *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SQUEEZE_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp
new file mode 100644
index 000000000..574fa3993
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2019 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 "StopGradientCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Log.h>
+
+namespace
+{
+
+bool canonicalize_stopgradient(loco::Graph *graph, moco::TFStopGradient *node)
+{
+ LOGGER(l);
+
+ INFO(l) << "TFNodeCanonicalize TFStopGradient begin";
+
+ /**
+ * This will replace shape inferred TFStopGradient node into canonical Forward
+ *
+ * Before
+ * In --- TFStopGradient --- Out(s)
+ *
+ * After
+ * -- TFStopGradient
+ * /
+ * In --- Forward --- Out(s)
+ */
+
+ // Create loco node to replace
+ auto forward_node = graph->nodes()->create<loco::Forward>();
+
+ // update connection
+ forward_node->input(node->input());
+
+ // replace node
+ replace(node).with(forward_node);
+
+ INFO(l) << "TFNodeCanonicalize TFStopGradient done";
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool StopGradientCanonicalizer::transform(TFStopGradient *node) const
+{
+ return canonicalize_stopgradient(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h
new file mode 100644
index 000000000..6a17728a6
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/StopGradientCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__
+#define __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+* @brief Canonicalize TF-dialect TFStopGradient into canonical Forward node
+*/
+class StopGradientCanonicalizer : public SimpleNodeTransform<moco::TFStopGradient>
+{
+public:
+ const char *name(void) const final { return "StopGradientCanonicalizer"; }
+
+public:
+ bool transform(moco::TFStopGradient *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_STOPGRADIENT_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp
new file mode 100644
index 000000000..c518b7d64
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 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 "SubCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include "TFEltwiseBinaryCanonicalzeHelper.h"
+
+namespace moco
+{
+namespace tf
+{
+
+bool SubCanonicalizer::transform(moco::TFSub *node) const
+{
+ return canonicalize_eltwise_binary_node(node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h
new file mode 100644
index 000000000..f715cc86c
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/SubCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_SUB_CANONICALIZER_H__
+#define __MOCO_TF_SUB_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFSub to Canonical EltwiseSub
+ */
+class SubCanonicalizer : public SimpleNodeTransform<moco::TFSub>
+{
+public:
+ const char *name(void) const final { return "SubCanonicalizer"; }
+
+public:
+ bool transform(moco::TFSub *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SUB_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp
new file mode 100644
index 000000000..081e0e5f9
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 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 "TFPushCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_push(loco::Graph *graph, moco::TFPush *node)
+{
+ /**
+ * @note This will replace TFRelu node with Canonical ReLU
+ *
+ * Before
+ * A --- TFPush
+ * After
+ * +- TFPush
+ * |
+ * A -+- Push
+ *
+ * Where
+ * A : from of TFPush
+ * TFPush will have no GraphOutputIndex
+ * Push will have GraphOutputIndex that from TFPush
+ */
+
+ auto push_node = graph->nodes()->create<loco::Push>();
+
+ auto node_A = node->from();
+
+ // update connections
+ push_node->from(node_A);
+
+ // update output index
+ push_node->index(node->index());
+ node->index_reset();
+
+ // replace node
+ replace(node).with(push_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool TFPushCanonicalizer::transform(TFPush *node) const
+{
+ return canonicalize_push(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h
new file mode 100644
index 000000000..569a71f82
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/TFPushCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_PUSH_CANONICALIZER_H__
+#define __MOCO_TF_PUSH_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFPush to Canonical Push
+ */
+class TFPushCanonicalizer : public SimpleNodeTransform<moco::TFPush>
+{
+public:
+ const char *name(void) const final { return "TFPushCanonicalizer"; }
+
+public:
+ bool transform(moco::TFPush *) const final;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PUSH_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp
new file mode 100644
index 000000000..3f48a50fc
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2019 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 "TanhCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <stdex/Memory.h>
+
+namespace
+{
+
+bool canonicalize_tanh(loco::Graph *graph, moco::TFTanh *node)
+{
+ /**
+ * @note This will replace TFTanh node with Canonical Tanh
+ *
+ * Before
+ * A --- TFTanh -- C
+ * After
+ * +- TFTanh --
+ * |
+ * A -+-- Tanh --- C
+ *
+ * Where
+ * A : x of TFTanh
+ * C : a node that uses TFTanh as an input
+ * TFTanh is disconnected from C
+ */
+
+ auto tanh_node = graph->nodes()->create<loco::Tanh>();
+
+ auto node_A = node->x();
+
+ // update connections
+ tanh_node->input(node_A);
+
+ // replace node
+ replace(node).with(tanh_node);
+
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool TanhCanonicalizer::transform(TFTanh *node) const
+{
+ return canonicalize_tanh(node->graph(), node);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h
new file mode 100644
index 000000000..af5e79fb5
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalization/TanhCanonicalizer.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_TANH_CANONICALIZER_H__
+#define __MOCO_TF_TANH_CANONICALIZER_H__
+
+#include "Transform.h"
+#include "SimpleNodeTransform.h"
+
+#include <moco/IR/TFNodes.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Convert TFTanh to Canonical Tanh
+ */
+class TanhCanonicalizer : public SimpleNodeTransform<moco::TFTanh>
+{
+public:
+ const char *name(void) const final { return "TanhCanonicalizer"; }
+
+public:
+ bool transform(moco::TFTanh *) const override;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_TANH_CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalizer.cpp b/compiler/moco-tf/src/Canonicalizer.cpp
new file mode 100644
index 000000000..04bc7c57a
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalizer.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2019 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 "Canonicalizer.h"
+
+#include "Knob.h"
+#include "ProgressReporter.h"
+
+#include "Transforms/ShapeInferencePass.h"
+#include "Transforms/TypeInferencePass.h"
+
+#include "Canonicalization/AddCanonicalizer.h"
+#include "Canonicalization/AvgPoolCanonicalizer.h"
+#include "Canonicalization/BiasAddCanonicalizer.h"
+#include "Canonicalization/ConcatV2Canonicalizer.h"
+#include "Canonicalization/ConstCanonicalizer.h"
+#include "Canonicalization/Conv2DBackpropInputCanonicalizer.h"
+#include "Canonicalization/Conv2DCanonicalizer.h"
+#include "Canonicalization/DepthwiseConv2dNativeCanonicalizer.h"
+#include "Canonicalization/IdentityCanonicalizer.h"
+#include "Canonicalization/MaximumCanonicalizer.h"
+#include "Canonicalization/MaxPoolCanonicalizer.h"
+#include "Canonicalization/MeanCanonicalizer.h"
+#include "Canonicalization/MulCanonicalizer.h"
+#include "Canonicalization/PadCanonicalizer.h"
+#include "Canonicalization/PlaceholderCanonicalizer.h"
+#include "Canonicalization/RealDivCanonicalizer.h"
+#include "Canonicalization/ReluCanonicalizer.h"
+#include "Canonicalization/Relu6Canonicalizer.h"
+#include "Canonicalization/ReshapeCanonicalizer.h"
+#include "Canonicalization/RsqrtCanonicalizer.h"
+#include "Canonicalization/SoftmaxCanonicalizer.h"
+#include "Canonicalization/SqrtCanonicalizer.h"
+#include "Canonicalization/SqueezeCanonicalizer.h"
+#include "Canonicalization/StopGradientCanonicalizer.h"
+#include "Canonicalization/SubCanonicalizer.h"
+#include "Canonicalization/TanhCanonicalizer.h"
+// For virtual nodes
+#include "Canonicalization/TFPushCanonicalizer.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+
+#include <logo/Phase.h>
+
+#include <stdex/Memory.h>
+
+#include <cassert>
+
+namespace
+{
+
+/**
+ * @brief Return true if graph has TFDialect nodes
+ */
+bool has_tf_nodes(loco::Graph *g)
+{
+ auto active_nodes = loco::active_nodes(loco::output_nodes(g));
+ for (auto node : active_nodes)
+ {
+ if (node->dialect() == moco::TFDialect::get())
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+void Canonicalizer::canonicalize(loco::Graph *g) const
+{
+ logo::Phase phase;
+
+ /* TRANSFORM DECLARATION BEGIN */
+ // Run shape and type inference at the top
+ phase.emplace_back(stdex::make_unique<ShapeInferencePass>());
+ phase.emplace_back(stdex::make_unique<TypeInferencePass>());
+
+ phase.emplace_back(stdex::make_unique<AddCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<AvgPoolCanonicalizer>());
+ if (moco::tf::get<moco::tf::Knob::CanonicalizeBiasAdd>())
+ phase.emplace_back(stdex::make_unique<BiasAddCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<ConcatV2Canonicalizer>());
+ if (moco::tf::get<moco::tf::Knob::CanonicalizeConst>())
+ phase.emplace_back(stdex::make_unique<ConstCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<Conv2DBackpropInputCanonicalizer>());
+ if (moco::tf::get<moco::tf::Knob::CanonicalizeConv2D>())
+ phase.emplace_back(stdex::make_unique<Conv2DCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<DepthwiseConv2dNativeCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<IdentityCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<MaximumCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<MaxPoolCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<MeanCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<MulCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<PadCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<PlaceholderCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<RealDivCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<ReluCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<Relu6Canonicalizer>());
+ phase.emplace_back(stdex::make_unique<ReshapeCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<RsqrtCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<SoftmaxCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<SqrtCanonicalizer>());
+ // NOTE SquaredDifference is handled in ResolveSquaredDifference
+ phase.emplace_back(stdex::make_unique<SqueezeCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<StopGradientCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<SubCanonicalizer>());
+ phase.emplace_back(stdex::make_unique<TanhCanonicalizer>());
+ // For virtual nodes
+ phase.emplace_back(stdex::make_unique<TFPushCanonicalizer>());
+ /* TRANSFORM DECLARATION END */
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Restart);
+ logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g};
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+
+ // Assert if graph has TF dialect nodes
+ assert(!has_tf_nodes(g));
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Canonicalizer.h b/compiler/moco-tf/src/Canonicalizer.h
new file mode 100644
index 000000000..098a6719c
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalizer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __CANONICALIZER_H__
+#define __CANONICALIZER_H__
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+class Canonicalizer final
+{
+public:
+ void canonicalize(loco::Graph *) const;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __CANONICALIZER_H__
diff --git a/compiler/moco-tf/src/Canonicalizer.test.cpp b/compiler/moco-tf/src/Canonicalizer.test.cpp
new file mode 100644
index 000000000..8eaf86f2f
--- /dev/null
+++ b/compiler/moco-tf/src/Canonicalizer.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 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 "Canonicalizer.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+// Canonicalizer SHOULD NOT crash even though a given graph is empty
+TEST(Canonicalizer, empty_graph)
+{
+ moco::tf::Canonicalizer cano;
+
+ loco::Graph g;
+
+ cano.canonicalize(&g);
+
+ SUCCEED();
+}
diff --git a/compiler/moco-tf/src/CodecHelper.h b/compiler/moco-tf/src/CodecHelper.h
new file mode 100644
index 000000000..85e4e2164
--- /dev/null
+++ b/compiler/moco-tf/src/CodecHelper.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __CODEC_HELPER_H__
+#define __CODEC_HELPER_H__
+
+#include <plier/tf/Convert.h>
+#include <stdex/Memory.h>
+
+namespace
+{
+
+using plier::tf::DataLayout;
+
+void set_feature_enc(loco::FeatureEncode *feature_enc, DataLayout data_layout)
+{
+ auto enc = stdex::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+
+ if (data_layout == DataLayout::NHWC)
+ {
+ enc->perm()->axis(loco::FeatureAxis::Count) = 0;
+ enc->perm()->axis(loco::FeatureAxis::Height) = 1;
+ enc->perm()->axis(loco::FeatureAxis::Width) = 2;
+ enc->perm()->axis(loco::FeatureAxis::Depth) = 3;
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ enc->perm()->axis(loco::FeatureAxis::Count) = 0;
+ enc->perm()->axis(loco::FeatureAxis::Depth) = 1;
+ enc->perm()->axis(loco::FeatureAxis::Height) = 2;
+ enc->perm()->axis(loco::FeatureAxis::Width) = 3;
+ }
+
+ feature_enc->encoder(std::move(enc));
+}
+
+void set_feature_dec(loco::FeatureDecode *feature_dec, DataLayout data_layout)
+{
+ auto dec = stdex::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+
+ if (data_layout == DataLayout::NHWC)
+ {
+ dec->perm()->axis(loco::FeatureAxis::Count) = 0;
+ dec->perm()->axis(loco::FeatureAxis::Height) = 1;
+ dec->perm()->axis(loco::FeatureAxis::Width) = 2;
+ dec->perm()->axis(loco::FeatureAxis::Depth) = 3;
+ }
+ else if (data_layout == DataLayout::NCHW)
+ {
+ dec->perm()->axis(loco::FeatureAxis::Count) = 0;
+ dec->perm()->axis(loco::FeatureAxis::Depth) = 1;
+ dec->perm()->axis(loco::FeatureAxis::Height) = 2;
+ dec->perm()->axis(loco::FeatureAxis::Width) = 3;
+ }
+
+ feature_dec->decoder(std::move(dec));
+}
+
+} // namespace
+
+#endif // __CODEC_HELPER_H__
diff --git a/compiler/moco-tf/src/Convert.cpp b/compiler/moco-tf/src/Convert.cpp
new file mode 100644
index 000000000..6285f5eab
--- /dev/null
+++ b/compiler/moco-tf/src/Convert.cpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. 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 "Convert.h"
+
+#include <algorithm>
+#include <cctype>
+
+// TODO move to some common file
+namespace moco
+{
+
+std::string str_toupper(std::string s)
+{
+ // from https://en.cppreference.com/w/cpp/string/byte/toupper
+ std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); });
+ return s;
+}
+
+} // namespace moco
diff --git a/compiler/moco-tf/src/Convert.h b/compiler/moco-tf/src/Convert.h
new file mode 100644
index 000000000..77dab3700
--- /dev/null
+++ b/compiler/moco-tf/src/Convert.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2019 Samsung Electronics Co., Ltd. All Rights Reserved
+ * Copyright 2017 The TensorFlow Authors. 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.
+ */
+
+#ifndef __CONVERT_H__
+#define __CONVERT_H__
+
+#include <string>
+
+// TODO move to some common file
+namespace moco
+{
+
+std::string str_toupper(std::string s);
+
+} // namespace moco
+
+#endif // __CONVERT_H__
diff --git a/compiler/moco-tf/src/Convert.test.cpp b/compiler/moco-tf/src/Convert.test.cpp
new file mode 100644
index 000000000..b02a597cb
--- /dev/null
+++ b/compiler/moco-tf/src/Convert.test.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2019 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 <Convert.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+
+TEST(moco_Convert, string_toupper)
+{
+ std::string source = "Hello World!!!";
+ std::string convert = moco::str_toupper(source);
+
+ ASSERT_EQ(convert, "HELLO WORLD!!!");
+}
diff --git a/compiler/moco-tf/src/Frontend.cpp b/compiler/moco-tf/src/Frontend.cpp
new file mode 100644
index 000000000..a17d5dd0e
--- /dev/null
+++ b/compiler/moco-tf/src/Frontend.cpp
@@ -0,0 +1,277 @@
+/*
+ * Copyright (c) 2019 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 <moco/tf/Frontend.h>
+#include <moco/Importer.h>
+#include <moco/IR/TFNode.h>
+#include <moco/Log.h>
+
+#include <moco/Import/GraphBuilderRegistry.h>
+
+#include "Canonicalizer.h"
+#include "Optimizer.h"
+#include "TFOptimizer.h"
+
+#include "Transforms.h"
+
+#include "Op/COpCall.h"
+
+#include <loco/Service/ShapeInference.h>
+
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <iostream>
+#include <sstream>
+#include <fstream>
+#include <stdexcept>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+namespace
+{
+
+bool load_text(std::istream *stream, tensorflow::GraphDef &graph_def)
+{
+ google::protobuf::io::IstreamInputStream iis(stream);
+
+ return google::protobuf::TextFormat::Parse(&iis, &graph_def);
+}
+
+bool load_binary(std::istream *stream, tensorflow::GraphDef &graph_def)
+{
+ google::protobuf::io::IstreamInputStream iis(stream);
+ google::protobuf::io::CodedInputStream cis(&iis);
+
+ return graph_def.ParseFromCodedStream(&cis);
+}
+
+void load_tf(std::istream *stream, moco::tf::Frontend::FileType type,
+ tensorflow::GraphDef &graph_def)
+{
+ bool result = (type == moco::tf::Frontend::FileType::Text) ? load_text(stream, graph_def)
+ : load_binary(stream, graph_def);
+ if (!result)
+ {
+ throw oops::UserExn("Failed to parse prototxt from stream");
+ }
+}
+
+// If Placeholder has no shape attribute, set unknown_rank property to true.
+void set_unknown_rank(tensorflow::GraphDef &tf_graph_def)
+{
+ for (auto &n : *tf_graph_def.mutable_node())
+ {
+ if (n.op().compare("Placeholder"))
+ continue;
+
+ auto iter = n.attr().find("shape");
+ if (iter == n.attr().end())
+ {
+ tensorflow::AttrValue attr;
+ attr.mutable_shape()->set_unknown_rank(true);
+ n.mutable_attr()->insert({"shape", attr});
+ }
+ }
+}
+
+/**
+ * @brief Set input shape according to signature if node has unknown shape in GraphDef.
+ *
+ * @note If shape you provided is wrong or not enough, it returns false.
+ */
+bool set_input_shape(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
+{
+ for (auto &n : *tf_graph_def.mutable_node())
+ {
+ if (n.op().compare("Placeholder"))
+ continue;
+
+ auto node_shape = n.mutable_attr()->at("shape").mutable_shape();
+ auto sig_shape = signature.shape(n.name() + ":0");
+
+ if (node_shape->unknown_rank() || !node_shape->dim_size())
+ {
+ // If shape in GraphDef is unknown, user must provide the shape info.
+ if (sig_shape == nullptr)
+ return false;
+ node_shape->clear_unknown_rank();
+ for (uint32_t i = 0; i < sig_shape->rank(); i++)
+ node_shape->add_dim()->set_size(-1);
+ }
+
+ for (uint32_t d = 0; d < node_shape->dim_size(); d++)
+ {
+ if (node_shape->mutable_dim(d)->size() == -1)
+ {
+ if (sig_shape == nullptr)
+ return false;
+ node_shape->mutable_dim(d)->set_size(sig_shape->dim(d));
+ }
+ else
+ {
+ // If User provide shape info though it already exists in GraphDef, make sure it matches
+ // the shape of GraphDef.
+ if (sig_shape && node_shape->dim(d).size() != sig_shape->dim(d))
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+void transform_tf(const moco::ModelSignature &signature, tensorflow::GraphDef &tf_graph_def)
+{
+ set_unknown_rank(tf_graph_def);
+ if (!set_input_shape(signature, tf_graph_def))
+ oops::UserExn("Info you provided may be wrong or not enough. Please check the info file.");
+}
+
+/**
+ * @brief Returns GraphBuilderRegistry that looks up default registry and additions
+ * such as custom op
+ */
+moco::GraphBuilderRegistry make_graph_builder_registry(const moco::ModelSignature &sig)
+{
+ moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()};
+
+ // build a COpCallGraphBuilder per custom op type
+ for (const auto &custom_op : sig.customops())
+ {
+ std::unique_ptr<moco::tf::COpCallGraphBuilder> builder =
+ stdex::make_unique<moco::tf::COpCallGraphBuilder>(&sig);
+ registry.add(custom_op, std::move(builder));
+ }
+
+ return registry;
+}
+
+} // namespace
+
+// TODO Find a proper place for this function
+
+namespace
+{
+
+loco::TensorShape tensor_shape(loco::Node *node)
+{
+ assert(loco::shape_known(node));
+ auto node_shape = loco::shape_get(node);
+ return node_shape.as<loco::TensorShape>();
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+Frontend::Frontend()
+{
+ // DO NOTHING
+}
+
+std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, const char *modelfile,
+ FileType type) const
+{
+ // Using c++ standard library, rather than file descriptor, makes these lines portable
+ std::ifstream ifs{modelfile, std::ios::in | std::ios::binary};
+ return load(signature, &ifs, type);
+}
+
+std::unique_ptr<loco::Graph> Frontend::load(const ModelSignature &signature, std::istream *stream,
+ FileType type) const
+{
+ tensorflow::GraphDef tf_graph_def;
+
+ load_tf(stream, type, tf_graph_def);
+
+ transform_tf(signature, tf_graph_def);
+
+ auto graph = import(signature, tf_graph_def);
+
+ return std::move(graph);
+}
+
+std::unique_ptr<loco::Graph> Frontend::import(const ModelSignature &signature,
+ tensorflow::GraphDef &tf_graph_def) const
+{
+ LOGGER(frontend);
+
+ // Let's use GraphBuilderRegistry with COpCallGraphBuilder
+ GraphBuilderRegistry registry = make_graph_builder_registry(signature);
+
+ Importer importer{&registry};
+
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> Import stage started";
+ INFO(frontend) << ">>";
+ auto graph = importer.import(signature, tf_graph_def);
+
+ TFOptimizer tfoptimizier;
+
+ // Transform TFNodes
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> TF optimize stage started";
+ INFO(frontend) << ">>";
+ tfoptimizier.optimize(graph.get());
+
+ // Fill graph-level input/output shape
+ //
+ // ASSUMPTION! All the shapes are known at this point
+ for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
+ {
+ auto input = graph->inputs()->at(n);
+ auto input_node = moco::placeholder_node(graph.get(), n);
+ assert(input_node != nullptr);
+ input->shape(stdex::make_unique<loco::TensorShape>(tensor_shape(input_node)));
+ }
+
+ for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
+ {
+ auto output = graph->outputs()->at(n);
+ auto output_node = moco::push_node(graph.get(), n);
+ assert(output_node != nullptr);
+ output->shape(stdex::make_unique<loco::TensorShape>(::tensor_shape(output_node)));
+ }
+
+ // Convert graph to hold only Canonical dialect
+ Canonicalizer canonicalizer;
+
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> Canonicalize stage started";
+ INFO(frontend) << ">>";
+ canonicalizer.canonicalize(graph.get());
+
+ // Optimize imported loco::Graph
+ Optimizer optimizer;
+
+ INFO(frontend) << ">>";
+ INFO(frontend) << ">> Canonical optimize stage started";
+ INFO(frontend) << ">>";
+ optimizer.optimize(graph.get());
+
+ return std::move(graph);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Frontend.test.cpp b/compiler/moco-tf/src/Frontend.test.cpp
new file mode 100644
index 000000000..c665bd9e3
--- /dev/null
+++ b/compiler/moco-tf/src/Frontend.test.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 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 "moco/tf/Frontend.h"
+
+#include "TestHelper.h"
+
+#include <sstream>
+
+#include <gtest/gtest.h>
+
+namespace
+{
+
+// clang-format off
+const char *pbtxt_000 = STRING_CONTENT(
+node {
+ name: "Placeholder"
+ op: "Placeholder"
+ attr {
+ key: "dtype"
+ value { type: DT_FLOAT }
+ }
+ attr {
+ key: "shape"
+ value {
+ shape {
+ dim { size: 4 }
+ }
+ }
+ }
+}
+node {
+ name: "Identity"
+ op: "Identity"
+ input: "Placeholder"
+ attr {
+ key: "T"
+ value { type: DT_FLOAT }
+ }
+}
+);
+// clang-format on
+
+} // namespace
+
+TEST(FrontendTests, testcase_000)
+{
+ moco::tf::Frontend frontend;
+ moco::ModelSignature signature;
+
+ signature.add_input(moco::TensorName("Placeholder", 0));
+ signature.shape("Placeholder:0", angkor::TensorShape{4});
+ signature.add_output(moco::TensorName("Identity", 0));
+
+ std::stringstream ss{pbtxt_000};
+
+ auto graph = frontend.load(signature, &ss, moco::tf::Frontend::FileType::Text);
+
+ ASSERT_EQ(graph->inputs()->size(), 1);
+ ASSERT_EQ(graph->inputs()->at(0)->name(), "Placeholder");
+ ASSERT_NE(graph->inputs()->at(0)->shape(), nullptr);
+ ASSERT_EQ(graph->inputs()->at(0)->shape()->rank(), 1);
+ ASSERT_EQ(graph->inputs()->at(0)->shape()->dim(0), 4);
+
+ ASSERT_EQ(graph->outputs()->size(), 1);
+ ASSERT_EQ(graph->outputs()->at(0)->name(), "Identity");
+ ASSERT_NE(graph->outputs()->at(0)->shape(), nullptr);
+ ASSERT_EQ(graph->outputs()->at(0)->shape()->rank(), 1);
+ ASSERT_EQ(graph->outputs()->at(0)->shape()->dim(0), 4);
+}
diff --git a/compiler/moco-tf/src/Knob.cpp b/compiler/moco-tf/src/Knob.cpp
new file mode 100644
index 000000000..0e1c7e0ea
--- /dev/null
+++ b/compiler/moco-tf/src/Knob.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2019 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 "Knob.h"
+
+#include <pepper/strcast.h>
+
+#include <iostream>
+#include <string>
+
+// Basic Infrastructure to declare and access Knob values
+//
+// TODO Reuse this infrastructure as a library
+namespace
+{
+
+using KnobName = std::string;
+
+/**
+ * @brief Load configuration (from somewhere)
+ */
+struct KnobLoader
+{
+ virtual ~KnobLoader() = default;
+
+ virtual bool load(const KnobName &name, bool default_value) const = 0;
+};
+
+// Template-programming helpers
+template <typename T> T knob_load(const KnobLoader &, const KnobName &, const T &);
+
+template <>
+bool knob_load(const KnobLoader &l, const KnobName &knob_name, const bool &default_value)
+{
+ return l.load(knob_name, default_value);
+}
+
+/**
+ * @brief Load configuration from environment variables
+ *
+ * Given a prefix P, EnvKnobLoader reads a configuration K from concat(P, K).
+ *
+ * For example, let us assume that P is "MY_" and K is "CONFIG".
+ *
+ * Then, EnvKnobLoader reads configuration CONFIG from environment variable MY_CONFIG.
+ */
+class EnvKnobLoader final : public KnobLoader
+{
+public:
+ EnvKnobLoader(const std::string &prefix) : _prefix{prefix}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool load(const KnobName &knob_name, bool default_value) const override
+ {
+ auto envvar = _prefix + knob_name;
+ auto s = std::getenv(envvar.c_str());
+
+ return pepper::safe_strcast<int>(s, default_value ? 1 : 0) != 0;
+ }
+
+private:
+ /// @brief Environment variable prefix
+ std::string _prefix;
+};
+
+} // namespace
+
+namespace
+{
+
+/**
+ * TODO Support Knob Loader Injection
+ *
+ * Let us assume that there is a compiler "A" based on moco, and it wants to reuse this
+ * infrastructure.
+ *
+ * Under the current design, users have to set "MOCO_XXX" even though they uses "A", which is
+ * counter-intuitive.
+ *
+ * "Knob Loader Injection" aims to address this issue. "Knob Loader Injection" allows "A" to
+ * inject its own knob loader that reads "A_XXX" environment variables.
+ */
+const KnobLoader &knob_loader(void)
+{
+ static EnvKnobLoader loader{"MOCO_"};
+ return loader;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+#define KNOB_BOOL(NAME, DEFAULT, DESC) \
+ template <> typename KnobTrait<Knob::NAME>::ValueType get<Knob::NAME>(void) \
+ { \
+ static typename KnobTrait<Knob::NAME>::ValueType value = \
+ ::knob_load<typename KnobTrait<Knob::NAME>::ValueType>(::knob_loader(), #NAME, DEFAULT); \
+ return value; \
+ }
+#include "Knob.lst"
+#undef KNOB_BOOL
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Knob.h b/compiler/moco-tf/src/Knob.h
new file mode 100644
index 000000000..145a81dc3
--- /dev/null
+++ b/compiler/moco-tf/src/Knob.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __KNOB_H__
+#define __KNOB_H__
+
+namespace moco
+{
+namespace tf
+{
+
+enum class Knob
+{
+#define KNOB_BOOL(NAME, DEFAULT, DESC) NAME,
+#include "Knob.lst"
+#undef KNOB_BOOL
+};
+
+template <Knob K> struct KnobTrait;
+
+#define KNOB_BOOL(NAME, DEFAULT, DESC) \
+ template <> struct KnobTrait<Knob::NAME> \
+ { \
+ using ValueType = bool; \
+ };
+#include "Knob.lst"
+#undef KNOB_BOOL
+
+template <Knob K> typename KnobTrait<K>::ValueType get(void);
+
+} // namespace tf
+} // namespace moco
+
+#endif // __KNOB_H__
diff --git a/compiler/moco-tf/src/Knob.lst b/compiler/moco-tf/src/Knob.lst
new file mode 100644
index 000000000..b88e064c7
--- /dev/null
+++ b/compiler/moco-tf/src/Knob.lst
@@ -0,0 +1,39 @@
+#ifndef KNOB_BOOL
+#error "KNOB_BOOL is not defined"
+#endif // KNOB_BOOL
+
+// KNOB_BOOL(NAME, DEFAULT_VALUE, DESCRIPTION)
+
+// TensorFlow dialect transforms
+KNOB_BOOL(FuseBinaryIntoPreceding, true, Fuse Binary node to preceding node)
+KNOB_BOOL(ResolveFusedBatchNorm, true, Enable ResolveFusedBatchNorm transform)
+KNOB_BOOL(ResolveConstantShape, true, Replace determined TFShape to TFConst)
+KNOB_BOOL(ResolveReshapeWildcardDim, true, Resolve wildcard dimension in TFReshape node)
+KNOB_BOOL(ResolveSquaredDifference, true, Resolve SquaredDifference node)
+KNOB_BOOL(RemoveTFIdentityNode, true, Enable RemoveTFIdentityNode optimization)
+KNOB_BOOL(SqueezeReduceNode, true, Insert TFSqueeze if ReduceNode do not keep dimensions)
+// Constant folding
+KNOB_BOOL(ConstantFoldAdd, false, Constant fold for Add node)
+KNOB_BOOL(ConstantFoldMul, false, Constant fold for Mul node)
+KNOB_BOOL(ConstantFoldPack, false, Constant fold for Pack node)
+KNOB_BOOL(ConstantFoldStridedSlice, false, Constant fold for StridedSlice node)
+
+// Canonicalization
+KNOB_BOOL(CanonicalizeBiasAdd, true, Enable Canonicalize for BiasAdd node)
+KNOB_BOOL(CanonicalizeConst, true, Enable Canonicalize for Const node)
+KNOB_BOOL(CanonicalizeConv2D, true, Enable Canonicalize for Conv2D node)
+
+// Canonical transforms
+KNOB_BOOL(ConstantFolding, true, Enable constant-folding optimization)
+KNOB_BOOL(RemoveForwardNode, true, Enable RemoveForwardNode optimization)
+KNOB_BOOL(ReorderDecode, true, Enable ReorderDecode optimization)
+// BEG: These knobs are valid only when ReorderDecode is enabled
+KNOB_BOOL(ReorderDecodeReLU, true, Reorder FeatureDecode-ReLU)
+KNOB_BOOL(ReorderDecodeTensorBiasAdd, true, Reorder FeatureDecode-TensorBiasAdd)
+// END
+KNOB_BOOL(SimplifyDomainConversion, true, Enable SimplifyDomainConversion optimization)
+KNOB_BOOL(ResolveDuplicateReshape, true, Resolve duplicated Reshape nodes)
+KNOB_BOOL(ResolveRedundantReshape, true, Resolve redundant Reshape node)
+
+// Graph transformations
+KNOB_BOOL(RemoveDeadNode, true, Enable RemoveDeadNode optimization)
diff --git a/compiler/moco-tf/src/LogHelper.cpp b/compiler/moco-tf/src/LogHelper.cpp
new file mode 100644
index 000000000..92ff75569
--- /dev/null
+++ b/compiler/moco-tf/src/LogHelper.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 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 "LogHelper.h"
+
+namespace loco
+{
+
+std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape)
+{
+ os << "[" << feature_shape.count().value() << "," << feature_shape.height().value() << ","
+ << feature_shape.width().value() << "," << feature_shape.depth().value() << "]";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape)
+{
+ os << "[" << filter_shape.height().value() << "," << filter_shape.width().value() << ","
+ << filter_shape.depth().value() << "," << filter_shape.count().value() << "]";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape)
+{
+ os << "[";
+ for (uint32_t r = 0; r < tensor_shape.rank(); ++r)
+ {
+ if (r)
+ os << ",";
+ os << tensor_shape.dim(r).value();
+ }
+ os << "]";
+ return os;
+}
+
+std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad)
+{
+ os << "[TLBR " << pad.top() << "," << pad.left() << "," << pad.bottom() << "," << pad.right()
+ << "]";
+
+ return os;
+}
+
+} // namespace loco
+
+std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64)
+{
+ for (auto vi : vi64)
+ {
+ os << vi << " ";
+ }
+ return os;
+}
+
+#include "TFFormattedGraph.h"
+
+namespace moco
+{
+namespace tf
+{
+
+FormattedGraph fmt(loco::Graph *g)
+{
+ auto node_summary_builder = stdex::make_unique<TFNodeSummaryBuilderFactory>();
+ return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder)));
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/LogHelper.h b/compiler/moco-tf/src/LogHelper.h
new file mode 100644
index 000000000..4e3cb5dac
--- /dev/null
+++ b/compiler/moco-tf/src/LogHelper.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __LOG_HELPER_H__
+#define __LOG_HELPER_H__
+
+#include <locop/FormattedGraph.h>
+
+#include <loco/IR/FeatureShape.h>
+#include <loco/IR/FilterShape.h>
+#include <loco/IR/TensorShape.h>
+
+#include <sstream>
+#include <vector>
+
+namespace loco
+{
+
+/**
+ * @brief dump FeatureShape values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const loco::FeatureShape &feature_shape);
+
+/**
+ * @brief dump FilterShape values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const loco::FilterShape &filter_shape);
+
+/**
+ * @brief dump TensorShape values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const loco::TensorShape &tensor_shape);
+
+/**
+ * @brief dump Padding2D values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const loco::Padding2D &pad);
+
+} // namespace loco
+
+/**
+ * @brief dump std::vector<int64_t> values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64);
+
+namespace moco
+{
+namespace tf
+{
+
+using FormattedGraph = locop::FormattedGraphImpl<locop::Formatter::LinearV1>;
+
+FormattedGraph fmt(loco::Graph *g);
+
+static inline FormattedGraph fmt(const std::unique_ptr<loco::Graph> &g) { return fmt(g.get()); }
+
+} // namespace tf
+} // namespace moco
+
+#endif // __LOG_HELPER_H__
diff --git a/compiler/moco-tf/src/Op/COpCall.cpp b/compiler/moco-tf/src/Op/COpCall.cpp
new file mode 100644
index 000000000..801196f0f
--- /dev/null
+++ b/compiler/moco-tf/src/Op/COpCall.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2019 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 "COpCall.h"
+
+#include "Convert.h"
+
+#include <locoex/COpCall.h>
+#include <locoex/COpAttrTypes.h>
+#include <moco/Names.h>
+#include <moco/tf/Frontend.h>
+#include <loco.h>
+#include <stdex/Memory.h>
+#include <oops/UserExn.h>
+
+#include <vector>
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+class COpCallGraphUpdate final : public moco::GraphUpdate
+{
+public:
+ COpCallGraphUpdate(locoex::COpCall *node, const std::vector<moco::TensorName> &input_names)
+ : _node(node), _input_names(input_names)
+ {
+ }
+
+ void input(const moco::SymbolTable *) const override;
+
+private:
+ locoex::COpCall *_node;
+ const std::vector<moco::TensorName> _input_names;
+};
+
+void COpCallGraphUpdate::input(const moco::SymbolTable *tensor_names) const
+{
+ for (int n = 0; n < _input_names.size(); n++)
+ {
+ loco::Node *target = tensor_names->node(_input_names.at(n));
+ _node->input(n, target);
+ }
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool COpCallGraphBuilder::validate(const tensorflow::NodeDef &tf_node) const { return true; }
+
+void COpCallGraphBuilder::build(const tensorflow::NodeDef &tf_node,
+ GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ loco::Graph *graph = context->graph();
+ SymbolTable *tensor_names = context->tensor_names();
+ UpdateQueue *updates = context->updates();
+
+ // Create a "COpCall" node for CustomOp and set attributes
+ auto call_node = graph->nodes()->create<locoex::COpCall>(tf_node.input_size());
+ {
+ call_node->op(tf_node.op());
+ call_node->name(tf_node.name());
+ call_node->dtype(_signature->dtype(tf_node.name()));
+
+ auto shape = _signature->shape(tf_node.name());
+ call_node->rank(shape->rank());
+ for (int d = 0; d < shape->rank(); d++)
+ call_node->dim(d) = shape->dim(d);
+
+ for (auto iter = tf_node.attr().begin(); iter != tf_node.attr().end(); iter++)
+ {
+ auto name = iter->first;
+ auto val = iter->second;
+
+ if (val.value_case() == tensorflow::AttrValue::kF)
+ {
+ call_node->attr(name, stdex::make_unique<locoex::COpAttrFloat>(val.f()));
+ }
+ else if (val.value_case() == tensorflow::AttrValue::kI)
+ {
+ call_node->attr(name, stdex::make_unique<locoex::COpAttrInt>(val.i()));
+ }
+ // TODO define more types
+ else
+ {
+ throw oops::UserExn("Unsupported attribute type", tf_node.name());
+ }
+ }
+ }
+
+ // register this node with its name
+ TensorName output_name(tf_node.name(), 0);
+ tensor_names->enroll(output_name, call_node);
+
+ // Queue node input update
+ std::vector<TensorName> input_names;
+ for (int i = 0; i < tf_node.input_size(); ++i)
+ {
+ input_names.emplace_back(TensorName(tf_node.input(i)));
+ }
+ auto update = stdex::make_unique<COpCallGraphUpdate>(call_node, input_names);
+ updates->enroll(std::move(update));
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Op/COpCall.h b/compiler/moco-tf/src/Op/COpCall.h
new file mode 100644
index 000000000..0bb8a93c9
--- /dev/null
+++ b/compiler/moco-tf/src/Op/COpCall.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __OP_COP_CALL_H__
+#define __OP_COP_CALL_H__
+
+#include <moco/tf/Frontend.h>
+
+#include <moco/Import/GraphBuilder.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief GraphBuilder for COpCall node
+ */
+class COpCallGraphBuilder final : public GraphBuilder
+{
+public:
+ COpCallGraphBuilder(const ModelSignature *signature) : _signature(signature) { /* empty */}
+ bool validate(const tensorflow::NodeDef &) const override;
+ void build(const tensorflow::NodeDef &, GraphBuilderContext *) const override;
+
+private:
+ const ModelSignature *_signature;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __OP_COP_CALL_H__
diff --git a/compiler/moco-tf/src/Op/COpCall.test.cpp b/compiler/moco-tf/src/Op/COpCall.test.cpp
new file mode 100644
index 000000000..f13118292
--- /dev/null
+++ b/compiler/moco-tf/src/Op/COpCall.test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019 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 "COpCall.h"
+
+#include "TestHelper.h"
+
+#include "Canonicalizer.h"
+
+#include <moco/Importer.h>
+
+#include <locoex/COpCall.h>
+#include <locoex/COpAttrTypes.h>
+
+#include <loco.h>
+#include <plier/tf/TestHelper.h>
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+using namespace moco::tf::test;
+
+namespace
+{
+// clang-format off
+const char *customop_01_pbtxtdata = STRING_CONTENT(
+node {
+ name: "input1"
+ op: "Placeholder"
+ attr {
+ key: "dtype" value { type: DT_FLOAT } }
+ attr {
+ key: "shape"
+ value { shape { dim { size: 1 } dim { size: 2 } } }
+ }
+}
+node {
+ name: "input2"
+ op: "Const"
+ attr { key: "dtype" value { type: DT_FLOAT } }
+ attr {
+ key: "value"
+ value {
+ tensor {
+ dtype: DT_FLOAT
+ tensor_shape { dim { size: 1 } dim { size: 2 } }
+ float_val: 1.1 float_val: 2.2
+ }
+ }
+ }
+}
+node {
+ name: "my/customOp/000"
+ op: "new_custom_op"
+ input: "input1"
+ input: "input2"
+ attr { key: "my_float" value { f: 0.001 } }
+ attr { key: "my_int" value { i: 111 } }
+}
+);
+
+// clang-format on
+} // namespace
+
+TEST(Call_Test, Call_01)
+{
+ moco::ModelSignature signature;
+ {
+ signature.add_input(moco::TensorName("input1", 0));
+ signature.add_output(moco::TensorName("my/customOp/000", 0));
+ signature.add_customop("new_custom_op");
+ signature.dtype("my/customOp/000", loco::DataType::FLOAT32);
+ signature.shape("my/customOp/000", {1, 2});
+ }
+
+ tensorflow::GraphDef graph_def;
+ EXPECT_TRUE(plier::tf::parse_graphdef(customop_01_pbtxtdata, graph_def));
+
+ // import
+ moco::GraphBuilderRegistry registry{&moco::GraphBuilderRegistry::get()};
+ registry.add("new_custom_op", stdex::make_unique<moco::tf::COpCallGraphBuilder>(&signature));
+
+ moco::Importer importer(&registry);
+ std::unique_ptr<loco::Graph> graph = importer.import(signature, graph_def);
+
+ // what to test:
+ // - there should exist COpCall
+ // - two input nodes should exist and not be nullptr
+ // - attributes should match
+
+ auto *customop = moco::tf::test::find_first_node_bytype<locoex::COpCall>(graph.get());
+ ASSERT_NE(customop, nullptr);
+
+ ASSERT_EQ(customop->arity(), 2);
+
+ loco::Node *input_0 = customop->arg(0);
+ loco::Node *input_1 = customop->arg(1);
+ ASSERT_NE(input_0, nullptr);
+ ASSERT_NE(input_1, nullptr);
+
+ auto f_attr = customop->attr<locoex::COpAttrType::Float>("my_float");
+ ASSERT_FLOAT_EQ(f_attr->val(), 0.001);
+ ASSERT_TRUE(f_attr->type() == locoex::COpAttrType::Float);
+
+ auto i_attr = customop->attr<locoex::COpAttrType::Int>("my_int");
+ ASSERT_FLOAT_EQ(i_attr->val(), 111);
+ ASSERT_TRUE(i_attr->type() == locoex::COpAttrType::Int);
+}
diff --git a/compiler/moco-tf/src/Optimizer.cpp b/compiler/moco-tf/src/Optimizer.cpp
new file mode 100644
index 000000000..f33b4109b
--- /dev/null
+++ b/compiler/moco-tf/src/Optimizer.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2019 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 "Optimizer.h"
+
+#include "Knob.h"
+#include "ProgressReporter.h"
+#include "Transforms.h"
+
+#include <logo/Phase.h>
+
+#include <stdex/Memory.h>
+
+namespace moco
+{
+namespace tf
+{
+
+void Optimizer::optimize(loco::Graph *g) const
+{
+ logo::Phase phase;
+
+ /* TRANSFORM DECLARATION BEGIN */
+ // Shape inference is required for ResolveRedundantReshape
+ phase.emplace_back(stdex::make_unique<ShapeInferencePass>());
+
+ if (moco::tf::get<moco::tf::Knob::ConstantFolding>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::ConstantFoldingPass>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::ReorderDecode>() &&
+ moco::tf::get<moco::tf::Knob::ReorderDecodeTensorBiasAdd>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::ReorderDecodePass<loco::TensorBiasAdd>>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::ReorderDecode>() &&
+ moco::tf::get<moco::tf::Knob::ReorderDecodeReLU>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::ReorderDecodePass<loco::ReLU>>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::SimplifyDomainConversion>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::SimplifyDomainConversionPass>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::RemoveForwardNode>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::RemoveForwardNodePass>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::ResolveDuplicateReshape>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::ResolveDuplicateReshapePass>());
+ }
+
+ if (moco::tf::get<moco::tf::Knob::ResolveRedundantReshape>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::ResolveRedundantReshapePass>());
+ }
+ /* TRANSFORM DECLARATION END */
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Saturate);
+ logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g};
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Optimizer.h b/compiler/moco-tf/src/Optimizer.h
new file mode 100644
index 000000000..8584df89b
--- /dev/null
+++ b/compiler/moco-tf/src/Optimizer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __OPTIMIZER_H__
+#define __OPTIMIZER_H__
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+class Optimizer final
+{
+public:
+ void optimize(loco::Graph *) const;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __OPTIMIZER_H__
diff --git a/compiler/moco-tf/src/Optimizer.test.cpp b/compiler/moco-tf/src/Optimizer.test.cpp
new file mode 100644
index 000000000..5ffed58e3
--- /dev/null
+++ b/compiler/moco-tf/src/Optimizer.test.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2019 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 "Optimizer.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+// Optimizer SHOULD NOT crash even though a given graph is empty
+TEST(Optimizer, empty_graph)
+{
+ moco::tf::Optimizer o;
+
+ loco::Graph g;
+
+ o.optimize(&g);
+
+ SUCCEED();
+}
+
+TEST(Optimizer, simple_forward_graph)
+{
+ moco::tf::Optimizer o;
+
+ /**
+ * Create a simple graph that forwards a constant as output
+ */
+ loco::Graph g;
+ {
+ auto constgen = g.nodes()->create<loco::ConstGen>();
+ constgen->shape({2, 3});
+
+ auto forward = g.nodes()->create<loco::Forward>();
+ forward->input(constgen);
+
+ auto pull = g.nodes()->create<loco::Push>();
+ pull->from(forward);
+ }
+
+ o.optimize(&g);
+
+ SUCCEED();
+}
+
+TEST(Optimizer, simple_forward_graph_with_one_valid_output)
+{
+ moco::tf::Optimizer o;
+
+ /**
+ * Create a simple graph that forwards a constant as graph-level output
+ */
+ loco::Graph g;
+ {
+ auto output = g.outputs()->create();
+
+ auto constgen = g.nodes()->create<loco::ConstGen>();
+ constgen->shape({2, 3});
+ constgen->dtype(loco::DataType::FLOAT32);
+ constgen->size<loco::DataType::FLOAT32>(6);
+
+ auto forward = g.nodes()->create<loco::Forward>();
+ forward->input(constgen);
+
+ auto pull = g.nodes()->create<loco::Push>();
+ pull->from(forward);
+
+ loco::link(output, pull);
+ }
+
+ o.optimize(&g);
+
+ SUCCEED();
+}
diff --git a/compiler/moco-tf/src/ProgressReporter.cpp b/compiler/moco-tf/src/ProgressReporter.cpp
new file mode 100644
index 000000000..41338ffec
--- /dev/null
+++ b/compiler/moco-tf/src/ProgressReporter.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2019 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 "ProgressReporter.h"
+
+#include "LogHelper.h"
+
+#include <logo/Phase.h>
+#include <logo/Pass.h>
+
+#include <moco/Log.h>
+
+#include <cassert>
+
+namespace
+{
+
+char to_char(bool b) { return b ? 'Y' : 'N'; }
+
+const char *to_str(logo::PhaseStrategy s)
+{
+ switch (s)
+ {
+ case logo::PhaseStrategy::Saturate:
+ return "Saturate";
+ case logo::PhaseStrategy::Restart:
+ return "Restart";
+ }
+ assert(false);
+ return "";
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *info)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "==============================================================";
+ INFO(prime) << "PhaseRunner<" << to_str(strategy()) << ">";
+ INFO(prime) << "Initial graph";
+ INFO(prime) << moco::tf::fmt(graph());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *info)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "PhaseRunner<" << to_str(strategy()) << "> - done";
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *info)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "--------------------------------------------------------------";
+ INFO(prime) << "Before " << logo::pass_name(info->pass());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *info)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "After " << logo::pass_name(info->pass())
+ << " (changed: " << to_char(info->changed()) << ")";
+ INFO(prime) << moco::tf::fmt(graph());
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/ProgressReporter.h b/compiler/moco-tf/src/ProgressReporter.h
new file mode 100644
index 000000000..190d972c5
--- /dev/null
+++ b/compiler/moco-tf/src/ProgressReporter.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_PROGRESSREPORTER_H__
+#define __MOCO_TF_PROGRESSREPORTER_H__
+
+#include <logo/Phase.h>
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+class ProgressReporter : public logo::PhaseEventListener
+{
+public:
+ ProgressReporter(loco::Graph *graph, logo::PhaseStrategy strategy)
+ : _graph{graph}, _strategy{strategy}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassBegin> *) override;
+ void notify(const logo::PhaseEventInfo<logo::PhaseEvent::PassEnd> *) override;
+
+public:
+ loco::Graph *graph(void) const { return _graph; }
+ logo::PhaseStrategy strategy(void) const { return _strategy; }
+
+private:
+ loco::Graph *_graph;
+ logo::PhaseStrategy _strategy;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_PROGRESSREPORTER_H__
diff --git a/compiler/moco-tf/src/SimpleNodeTransform.h b/compiler/moco-tf/src/SimpleNodeTransform.h
new file mode 100644
index 000000000..b69cbad6b
--- /dev/null
+++ b/compiler/moco-tf/src/SimpleNodeTransform.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__
+#define __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__
+
+#include "Transform.h"
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Per-Node Transform
+ */
+template <typename ConcreteNode> struct SimpleNodeTransform : public Transform
+{
+ SimpleNodeTransform() = default;
+
+ virtual ~SimpleNodeTransform() = default;
+
+ // NOTE Users SHOULD implement this method
+ virtual bool transform(ConcreteNode *node) const = 0;
+
+ bool run(loco::Graph *graph) final
+ {
+ using loco::active_nodes;
+ using loco::output_nodes;
+
+ bool changed = false;
+
+ for (auto node : active_nodes(output_nodes(graph)))
+ {
+ if (auto casted = dynamic_cast<ConcreteNode *>(node))
+ {
+ if (transform(casted))
+ {
+ changed = true;
+ }
+ }
+ }
+
+ return changed;
+ }
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SIMPLE_NODE_TRANSFORM_H__
diff --git a/compiler/moco-tf/src/SimpleNodeTransform.test.cpp b/compiler/moco-tf/src/SimpleNodeTransform.test.cpp
new file mode 100644
index 000000000..781a48781
--- /dev/null
+++ b/compiler/moco-tf/src/SimpleNodeTransform.test.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 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 "SimpleNodeTransform.h"
+
+#include <set>
+
+#include <gtest/gtest.h>
+
+TEST(SimpleNodeTransformTests, run)
+{
+ class Transform final : public moco::tf::SimpleNodeTransform<loco::Push>
+ {
+ public:
+ Transform(std::multiset<loco::Node *> *out) : _out{out}
+ {
+ // DO NOTHING
+ }
+
+ public:
+ bool transform(loco::Push *node) const final
+ {
+ _out->insert(node);
+ return false;
+ }
+
+ private:
+ std::multiset<loco::Node *> *_out;
+ };
+
+ auto g = loco::make_graph();
+ auto output_0 = g->outputs()->create();
+ auto push = g->nodes()->create<loco::Push>();
+ loco::link(output_0, push);
+
+ std::multiset<loco::Node *> nodes;
+ Transform transform{&nodes};
+
+ transform.run(g.get());
+
+ ASSERT_EQ(nodes.size(), 1);
+ ASSERT_EQ(nodes.count(push), 1);
+}
diff --git a/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h b/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h
new file mode 100644
index 000000000..df9aec144
--- /dev/null
+++ b/compiler/moco-tf/src/TFEltwiseBinaryCanonicalzeHelper.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__
+#define __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+
+#include "CanonicalEltwiseInputConnector.h"
+#include "BroadcastHelper.h"
+
+#include <loco/IR/Nodes.h>
+#include <loco/IR/NodeShape.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <fipe.h>
+
+namespace
+{
+
+template <typename TFNodeT> struct EltwiseBinaryCanonicalizationRule;
+
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFAdd>
+{
+ using CanonicalNode = loco::EltwiseAdd;
+};
+
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFSub>
+{
+ using CanonicalNode = loco::EltwiseSub;
+};
+
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFMaximum>
+{
+ using CanonicalNode = loco::EltwiseMax;
+};
+
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFMul>
+{
+ using CanonicalNode = loco::EltwiseMul;
+};
+
+template <> struct EltwiseBinaryCanonicalizationRule<moco::TFRealDiv>
+{
+ using CanonicalNode = loco::EltwiseDiv;
+};
+
+template <typename TFNode> bool canonicalize_eltwise_binary_node(TFNode *node)
+{
+ auto graph = node->graph();
+
+ /**
+ * This will replace T/F Eltwise Binary node with a corresponding Canonical Eltwise node
+ *
+ * BEFORE
+ * A --- T/F Node --- C
+ * /
+ * B ----
+ *
+ * AFTER
+ * A --- T/F Node ---
+ * /
+ * B ----
+ *
+ * A --- [FixedReshape] --- [TensorBroadcast] --- Canonical Node -- C
+ * /
+ * B --- [FixedReshape] --- [TensorBroadcast] ----
+ *
+ * NOTE
+ * - [...] means optional node. They may or may not be created during this procedure.
+ * - T/F Node is disconnected from C after transformation.
+ */
+
+ using CanonicalNodeT = typename EltwiseBinaryCanonicalizationRule<TFNode>::CanonicalNode;
+
+ auto node_A = node->x();
+ auto node_B = node->y();
+
+ if (!loco::shape_known(node_A) || !loco::shape_known(node_B))
+ return false;
+ if (!loco::shape_known(node))
+ return false;
+
+ auto out_shape = loco::shape_get(node).template as<loco::TensorShape>();
+
+ // Create a node
+ auto canonical_node = graph->nodes()->template create<CanonicalNodeT>();
+
+ using moco::tf::eltwise::binary::connect_to;
+ using moco::tf::broadcast_to;
+
+ // update connections
+ std::make_pair(node_A, node_B) | broadcast_to(out_shape) | connect_to(canonical_node);
+
+ // replace node
+ replace(node).with(canonical_node);
+
+ return true;
+}
+
+} // namespace
+
+#endif // __TF_ELTWISE_BINARY_CANONICALIZE_HELPER_H__
diff --git a/compiler/moco-tf/src/TFFormattedGraph.cpp b/compiler/moco-tf/src/TFFormattedGraph.cpp
new file mode 100644
index 000000000..2ea514a2b
--- /dev/null
+++ b/compiler/moco-tf/src/TFFormattedGraph.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2019 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 "TFFormattedGraph.h"
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+
+#include "LogHelper.h"
+
+#include <pepper/str.h>
+#include <locoex/Service/COpFormattedGraph.h>
+#include <oops/InternalExn.h>
+
+#include <sstream>
+
+namespace
+{
+
+std::string opname(uint32_t opnum)
+{
+ static std::string prefix{"tf."};
+
+ switch (static_cast<moco::TFOpcode>(opnum))
+ {
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ case moco::TFOpcode::OPCODE: \
+ return prefix + #OPCODE;
+#include <moco/IR/TFNodes.lst>
+#undef TENSORFLOW_NODE
+ default:
+ break;
+ };
+
+ return prefix + "Invalid";
+}
+
+using namespace moco;
+using namespace moco::tf;
+
+/// TFNodeSummaryBuilder with default implementation
+class TFNodeSummaryBuilderBase : public locop::NodeSummaryBuilder
+{
+public:
+ TFNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *, locop::NodeSummary &s) const final;
+
+protected:
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ virtual bool summary(const CLASS *node, locop::NodeSummary &s) const \
+ { \
+ s.comments().append("Emitted by Default NodeSummaryBuilder"); \
+ s.state(locop::NodeSummary::State::PartiallyKnown); \
+ return true; \
+ }
+#include <moco/IR/TFNodes.lst>
+#undef TENSORFLOW_NODE
+
+protected:
+ const locop::SymbolTable *tbl(void) const { return _tbl; }
+
+ // Please do not use _tbl directly and use tbl().
+ // This will be changed to private in near future.
+protected:
+ const locop::SymbolTable *_tbl;
+};
+
+class TFNodeSummaryBuilder final : public TFNodeSummaryBuilderBase
+{
+public:
+ TFNodeSummaryBuilder(const locop::SymbolTable *tbl) : TFNodeSummaryBuilderBase(tbl)
+ {
+ // DO NOTHING
+ }
+
+private:
+#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final
+ IMPLEMENT(TFAdd);
+ IMPLEMENT(TFAvgPool);
+ IMPLEMENT(TFBiasAdd);
+ IMPLEMENT(TFConcatV2);
+ IMPLEMENT(TFConst);
+ IMPLEMENT(TFConv2D);
+ IMPLEMENT(TFConv2DBackpropInput);
+ IMPLEMENT(TFDepthwiseConv2dNative);
+ IMPLEMENT(TFFusedBatchNorm);
+ IMPLEMENT(TFMaximum);
+ IMPLEMENT(TFMaxPool);
+ IMPLEMENT(TFMean);
+ IMPLEMENT(TFMul);
+ IMPLEMENT(TFPack);
+ IMPLEMENT(TFReshape);
+ IMPLEMENT(TFRsqrt);
+ IMPLEMENT(TFShape);
+ IMPLEMENT(TFSoftmax);
+ IMPLEMENT(TFSqueeze);
+ IMPLEMENT(TFStopGradient);
+ IMPLEMENT(TFStridedSlice);
+ IMPLEMENT(TFTanh);
+ // For virtual nodes
+ IMPLEMENT(TFPush);
+#undef IMPLEMENT
+};
+
+bool TFNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (node->dialect() != TFDialect::get())
+ return false;
+
+#define TENSORFLOW_NODE(OPCODE, CLASS) \
+ if (dynamic_cast<const CLASS *>(node)) \
+ { \
+ s.opname(opname(node->opnum())); \
+ return summary(dynamic_cast<const CLASS *>(node), s); \
+ }
+#include <moco/IR/TFNodes.lst>
+#undef TENSORFLOW_NODE
+
+ return false;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFAdd *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFAvgPool *node, locop::NodeSummary &s) const
+{
+ s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("ksize", pepper::str(node->ksize()));
+ s.args().append("strides", pepper::str(node->strides()));
+ s.args().append("padding", node->padding());
+ s.args().append("data_layout", node->data_layout());
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFBiasAdd *node, locop::NodeSummary &s) const
+{
+ s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("bias", tbl()->lookup(node->bias()));
+ s.args().append("data_layout", node->data_layout());
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFConcatV2 *node, locop::NodeSummary &s) const
+{
+ for (uint32_t n = 0; n < node->num_values(); ++n)
+ {
+ std::ostringstream ss;
+ ss << "values(" << n << ")";
+ s.args().append(ss.str(), tbl()->lookup(node->values(n)));
+ }
+ s.args().append("axis", tbl()->lookup(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFConst *node, locop::NodeSummary &s) const
+{
+ std::ostringstream ss;
+
+ auto dtype = node->dtype();
+ switch (dtype)
+ {
+ case loco::DataType::S32:
+ ss << node->size<loco::DataType::S32>();
+ break;
+ case loco::DataType::FLOAT32:
+ ss << node->size<loco::DataType::FLOAT32>();
+ break;
+ default:
+ INTERNAL_EXN_V("Unsupported data type", node->name());
+ }
+ s.args().append("size", ss.str());
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFConv2D *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("padding", node->padding());
+ s.args().append("data_layout", node->data_layout());
+ s.args().append("strides", pepper::str(node->strides()));
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFConv2DBackpropInput *node, locop::NodeSummary &s) const
+{
+ s.args().append("input_sizes", tbl()->lookup(node->input_sizes()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("out_backprop", tbl()->lookup(node->out_backprop()));
+ s.args().append("padding", node->padding());
+ s.args().append("data_layout", node->data_layout());
+ s.args().append("strides", pepper::str(node->strides()));
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFDepthwiseConv2dNative *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("padding", node->padding());
+ s.args().append("data_layout", node->data_layout());
+ s.args().append("strides", pepper::str(node->strides()));
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFFusedBatchNorm *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("scale", tbl()->lookup(node->scale()));
+ s.args().append("offset", tbl()->lookup(node->offset()));
+ s.args().append("mean", tbl()->lookup(node->mean()));
+ s.args().append("variance", tbl()->lookup(node->variance()));
+ s.args().append("epsilon", pepper::str(node->epsilon()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFMaximum *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFMaxPool *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("ksize", pepper::str(node->ksize()));
+ s.args().append("strides", pepper::str(node->strides()));
+ s.args().append("padding", node->padding());
+ s.args().append("data_layout", node->data_layout());
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFMean *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("reduction_indices", tbl()->lookup(node->reduction_indices()));
+ s.args().append("keep_dims", pepper::str(node->keep_dims()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFMul *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFPack *node, locop::NodeSummary &s) const
+{
+ s.args().append("N", pepper::str(node->N()));
+ s.args().append("axis", pepper::str(node->axis()));
+ for (uint32_t n = 0; n < node->N(); ++n)
+ s.args().append("values", tbl()->lookup(node->values(n)));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFReshape *node, locop::NodeSummary &s) const
+{
+ s.args().append("tensor", tbl()->lookup(node->tensor()));
+ s.args().append("shape", tbl()->lookup(node->shape()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFRsqrt *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFShape *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFSoftmax *node, locop::NodeSummary &s) const
+{
+ s.args().append("logits", tbl()->lookup(node->logits()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFSqueeze *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("squeeze_dims", pepper::str(node->squeeze_dims()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFStopGradient *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFStridedSlice *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("begin", tbl()->lookup(node->begin()));
+ s.args().append("end", tbl()->lookup(node->end()));
+ if (node->strides() != nullptr)
+ s.args().append("strides", tbl()->lookup(node->strides()));
+ s.args().append("begin_mask", pepper::str(node->begin_mask()));
+ s.args().append("end_mask", pepper::str(node->end_mask()));
+ s.args().append("ellipsis_mask", pepper::str(node->ellipsis_mask()));
+ s.args().append("new_axis_mask", pepper::str(node->new_axis_mask()));
+ s.args().append("shrink_axis_mask", pepper::str(node->shrink_axis_mask()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool TFNodeSummaryBuilder::summary(const TFTanh *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+// For virtual nodes
+bool TFNodeSummaryBuilder::summary(const TFPush *node, locop::NodeSummary &s) const
+{
+ s.args().append("index", node->indexed() ? pepper::str(node->index()) : "?");
+ s.args().append("from", tbl()->lookup(node->from()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+} // namespace
+
+namespace moco
+{
+namespace tf
+{
+
+bool MocoNodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (TFNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (locoex::COpNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/TFFormattedGraph.h b/compiler/moco-tf/src/TFFormattedGraph.h
new file mode 100644
index 000000000..f79208536
--- /dev/null
+++ b/compiler/moco-tf/src/TFFormattedGraph.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __TF_FORMATTED_GRAPH_H__
+#define __TF_FORMATTED_GRAPH_H__
+
+#include <locop/FormattedGraph.h>
+
+#include <stdex/Memory.h>
+
+namespace moco
+{
+namespace tf
+{
+
+class MocoNodeSummaryBuilder final : public locop::NodeSummaryBuilder
+{
+public:
+ MocoNodeSummaryBuilder(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *node, locop::NodeSummary &s) const final;
+
+private:
+ const locop::SymbolTable *_tbl;
+};
+
+class TFNodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory
+{
+public:
+ TFNodeSummaryBuilderFactory() = default;
+
+public:
+ std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final
+ {
+ return stdex::make_unique<MocoNodeSummaryBuilder>(tlb);
+ }
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __TF_FORMATTED_GRAPH_H__
diff --git a/compiler/moco-tf/src/TFOptimizer.cpp b/compiler/moco-tf/src/TFOptimizer.cpp
new file mode 100644
index 000000000..2256b99b8
--- /dev/null
+++ b/compiler/moco-tf/src/TFOptimizer.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2019 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 "TFOptimizer.h"
+
+#include "Knob.h"
+#include "ProgressReporter.h"
+#include "Transforms.h"
+
+#include <logo/Phase.h>
+
+#include <stdex/Memory.h>
+
+namespace moco
+{
+namespace tf
+{
+
+void TFOptimizer::optimize(loco::Graph *g) const
+{
+ logo::Phase phase;
+
+ /* TRANSFORM DECLARATION BEGIN */
+ if (moco::tf::get<moco::tf::Knob::ResolveFusedBatchNorm>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::ResolveFusedBatchNorm>());
+ }
+ if (moco::tf::get<moco::tf::Knob::FuseBinaryIntoPreceding>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::FuseBinaryIntoPreceding>());
+ }
+ if (moco::tf::get<moco::tf::Knob::ResolveConstantShape>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::ResolveConstantShape>());
+ }
+ if (moco::tf::get<moco::tf::Knob::ResolveReshapeWildcardDim>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::ResolveReshapeWildcardDim>());
+ }
+ if (moco::tf::get<moco::tf::Knob::ResolveSquaredDifference>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::ResolveSquaredDifference>());
+ }
+ if (moco::tf::get<moco::tf::Knob::RemoveTFIdentityNode>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::RemoveTFIdentityNode>());
+ }
+ if (moco::tf::get<moco::tf::Knob::RemoveDeadNode>())
+ {
+ phase.emplace_back(stdex::make_unique<logo::RemoveDeadNodePass>());
+ }
+ if (moco::tf::get<moco::tf::Knob::SqueezeReduceNode>())
+ {
+ phase.emplace_back(stdex::make_unique<moco::SqueezeReduceNode>());
+ }
+ // Shape inference is needed for added nodes doing above transformations
+ phase.emplace_back(stdex::make_unique<moco::tf::ShapeInferencePass>());
+ phase.emplace_back(stdex::make_unique<moco::tf::TypeInferencePass>());
+ /* TRANSFORM DECLARATION END */
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Saturate);
+ logo::PhaseRunner<logo::PhaseStrategy::Saturate> phase_runner{g};
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/TFOptimizer.h b/compiler/moco-tf/src/TFOptimizer.h
new file mode 100644
index 000000000..69ab74d3e
--- /dev/null
+++ b/compiler/moco-tf/src/TFOptimizer.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __TF_OPTIMIZER_H__
+#define __TF_OPTIMIZER_H__
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+class TFOptimizer final
+{
+public:
+ void optimize(loco::Graph *) const;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __TF_OPTIMIZER_H__
diff --git a/compiler/moco-tf/src/TFOptimizer.test.cpp b/compiler/moco-tf/src/TFOptimizer.test.cpp
new file mode 100644
index 000000000..26348f6c8
--- /dev/null
+++ b/compiler/moco-tf/src/TFOptimizer.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2019 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 "TFOptimizer.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+// TFOptimizer SHOULD NOT crash even though a given graph is empty
+TEST(TFOptimizer, empty_graph)
+{
+ moco::tf::TFOptimizer tfo;
+
+ loco::Graph g;
+
+ tfo.optimize(&g);
+
+ SUCCEED();
+}
diff --git a/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h b/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h
new file mode 100644
index 000000000..abd24cec8
--- /dev/null
+++ b/compiler/moco-tf/src/TFReduceCanonicalzeHelper.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __TF_REDUCE_CANONICALIZE_HELPER_H__
+#define __TF_REDUCE_CANONICALIZE_HELPER_H__
+
+#include <moco/IR/TFDialect.h>
+#include <moco/IR/TFNodes.h>
+
+#include <loco/Service/ShapeInference.h>
+
+#include <moco/Log.h>
+
+namespace
+{
+
+template <typename TFNodeT> loco::ReduceFunc reduceFunc(void);
+
+template <> loco::ReduceFunc reduceFunc<moco::TFMean>(void) { return loco::ReduceFunc::Mean; }
+
+template <typename TFNode> bool canonicalize_reduce_node(TFNode *node)
+{
+ LOGGER(l);
+
+ INFO(l) << "TFNodeCanonicalize ReduceNode begin";
+
+ auto graph = node->graph();
+
+ /**
+ * This will replace T/F Reduce node with a corresponding Canonical Reduce node
+ *
+ * BEFORE
+ * reduction_indices -------- T/F Node -- C
+ * input -------/
+ *
+ * AFTER
+ * +------ T/F Node --
+ * | /
+ * reduction_indices -------
+ * | \
+ * input -+------ Canonical Node -- C
+ *
+ * NOTE
+ * - T/F Node is disconnected from C after transformation
+ */
+
+ // TFSqueeze had to be inserted if keep_dims() was false
+ assert(node->keep_dims());
+
+ auto axes_node = node->reduction_indices();
+ assert(axes_node != nullptr);
+
+ auto node_tensor_shape = loco::shape_get(node).template as<loco::TensorShape>();
+
+ // Canonicalization into TensorReduce is valid when reduction indices is constant
+ // TODO Support general TensorReduce case
+ std::vector<int32_t> axes_values;
+ if (auto const_axes = dynamic_cast<moco::TFConst *>(axes_node))
+ {
+ // TODO Support S64 type
+ assert(const_axes->dtype() == loco::DataType::S32);
+
+ for (uint32_t i = 0; i < const_axes->size<loco::DataType::S32>(); ++i)
+ {
+ int32_t axis = const_axes->at<loco::DataType::S32>(i);
+ if (axis < 0)
+ axis += node_tensor_shape.rank();
+ axes_values.push_back(axis);
+ }
+ }
+ else if (auto const_axes = dynamic_cast<loco::ConstGen *>(axes_node))
+ {
+ // TODO Support S64 type
+ assert(const_axes->dtype() == loco::DataType::S32);
+
+ for (uint32_t i = 0; i < const_axes->size<loco::DataType::S32>(); ++i)
+ {
+ int32_t axis = const_axes->at<loco::DataType::S32>(i);
+ if (axis < 0)
+ axis += node_tensor_shape.rank();
+ axes_values.push_back(axis);
+ }
+ }
+ else
+ return false;
+
+ // Create loco node to replace
+ auto reduce = graph->nodes()->template create<loco::TensorReduce>();
+
+ // replace
+ reduce->func(reduceFunc<TFNode>());
+ reduce->input(node->input());
+ for (uint32_t i = 0; i < axes_values.size(); ++i)
+ reduce->axes()->insert(axes_values.at(i));
+
+ replace(node).with(reduce);
+
+ INFO(l) << "TFNodeCanonicalize ReduceNode done";
+
+ return true;
+}
+
+} // namespace
+
+#endif // __TF_REDUCE_CANONICALIZE_HELPER_H__
diff --git a/compiler/moco-tf/src/TestHelper.h b/compiler/moco-tf/src/TestHelper.h
new file mode 100644
index 000000000..dd32d4433
--- /dev/null
+++ b/compiler/moco-tf/src/TestHelper.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __TEST_HELPER_H__
+#define __TEST_HELPER_H__
+
+#include <loco.h>
+
+#include <tensorflow/core/framework/graph.pb.h>
+
+#define STRING_CONTENT(content) #content
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+template <typename T> T *find_first_node_bytype(loco::Graph *g)
+{
+ T *first_node = nullptr;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ first_node = dynamic_cast<T *>(nodes->at(i));
+ if (first_node != nullptr)
+ break;
+ }
+
+ return first_node;
+}
+
+template <typename T> std::vector<T *> find_nodes_bytype(loco::Graph *g)
+{
+ std::vector<T *> find_nodes;
+ loco::Graph::NodeContext *nodes = g->nodes();
+ uint32_t count = nodes->size();
+
+ for (uint32_t i = 0; i < count; ++i)
+ {
+ auto node = dynamic_cast<T *>(nodes->at(i));
+ if (node != nullptr)
+ find_nodes.push_back(node);
+ }
+
+ return find_nodes;
+}
+
+/**
+ * @brief Append setup output of graph by adding loco::Push node
+ *
+ * @note This is subject to change when loco changes I/O treatment
+ */
+void setup_output_node(loco::Graph *graph, loco::Node *last_node);
+
+} // namespace test
+} // namespace tf
+} // namespace moco
+
+#include <moco/IR/TFNode.h>
+
+#include <moco/Import/GraphBuilder.h>
+
+#include <plier/tf/TestHelper.h>
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+class TFNodeBuildTester
+{
+public:
+ TFNodeBuildTester();
+
+public:
+ void inputs(const std::vector<std::string> &names);
+ void output(const char *name);
+ moco::TFNode *output(void);
+
+ void run(tensorflow::NodeDef &node_def, moco::GraphBuilder &graph_builder);
+
+private:
+ std::unique_ptr<moco::SymbolTable> _tensor_names;
+ std::unique_ptr<loco::Graph> _graph;
+
+ std::vector<moco::TFNode *> _inputs;
+ const char *_output{nullptr};
+};
+
+} // namespace test
+} // namespace tf
+} // namespace moco
+
+#endif // __TEST_HELPER_H__
diff --git a/compiler/moco-tf/src/TestHelper.test.cpp b/compiler/moco-tf/src/TestHelper.test.cpp
new file mode 100644
index 000000000..1e8c38e36
--- /dev/null
+++ b/compiler/moco-tf/src/TestHelper.test.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2019 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 "TestHelper.h"
+
+#include <google/protobuf/io/coded_stream.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+#include <google/protobuf/text_format.h>
+
+#include <cstring>
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+void setup_output_node(loco::Graph *graph, loco::Node *last_node)
+{
+ // add push as output
+ auto push_node = graph->nodes()->create<loco::Push>();
+ push_node->from(last_node);
+
+ // set the graph output name and node object
+ auto graph_output = graph->outputs()->create();
+ graph_output->name("output");
+ graph_output->dtype(loco::DataType::FLOAT32);
+ loco::link(graph_output, push_node);
+}
+
+} // namespace test
+} // namespace tf
+} // namespace moco
+
+#include <moco/IR/Nodes/TFConst.h>
+
+#include <stdex/Memory.h>
+
+#include <gtest/gtest.h>
+
+namespace moco
+{
+namespace tf
+{
+namespace test
+{
+
+TFNodeBuildTester::TFNodeBuildTester()
+{
+ _graph = loco::make_graph();
+ _tensor_names = stdex::make_unique<moco::SymbolTable>();
+}
+
+void TFNodeBuildTester::inputs(const std::vector<std::string> &names)
+{
+ for (auto name : names)
+ {
+ auto input = _graph->nodes()->create<moco::TFConst>();
+ moco::TensorName name_01(name, 0);
+ _tensor_names->enroll(name_01, input);
+
+ _inputs.push_back(input);
+ }
+}
+
+void TFNodeBuildTester::output(const char *name) { _output = name; }
+
+moco::TFNode *TFNodeBuildTester::output(void)
+{
+ assert(_output != nullptr);
+
+ moco::TensorName tname(_output, 0);
+ return static_cast<moco::TFNode *>(_tensor_names->node(tname));
+}
+
+void TFNodeBuildTester::run(tensorflow::NodeDef &nodedef, moco::GraphBuilder &graphbuilder)
+{
+ assert(_output != nullptr);
+
+ auto node_defs = stdex::make_unique<moco::NodeDefTable>();
+ auto updates = stdex::make_unique<moco::UpdateQueue>();
+
+ moco::GraphBuilderContext gb_context(_graph.get(), node_defs.get(), _tensor_names.get(),
+ updates.get());
+
+ EXPECT_TRUE(graphbuilder.validate(nodedef));
+ graphbuilder.build(nodedef, &gb_context);
+
+ for (auto &update : updates->queue())
+ {
+ update->input(_tensor_names.get());
+ }
+
+ auto tfnode = output();
+ ASSERT_NE(tfnode, nullptr);
+
+ int idx = 0;
+ ASSERT_EQ(tfnode->arity(), _inputs.size());
+ for (auto input : _inputs)
+ {
+ ASSERT_EQ(tfnode->arg(idx++), input);
+ }
+}
+
+} // namespace test
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Transform.cpp b/compiler/moco-tf/src/Transform.cpp
new file mode 100644
index 000000000..f19ce21c4
--- /dev/null
+++ b/compiler/moco-tf/src/Transform.cpp
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2019 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 "Transform.h"
+
+namespace moco
+{
+namespace tf
+{
+
+std::string transform_name(const Transform *t)
+{
+ if (t->name() == nullptr)
+ {
+ return "(unknown)";
+ }
+
+ return t->name();
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Transform.h b/compiler/moco-tf/src/Transform.h
new file mode 100644
index 000000000..80cb9f97f
--- /dev/null
+++ b/compiler/moco-tf/src/Transform.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_TRANSFORM_H__
+#define __MOCO_TF_TRANSFORM_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+#include <string>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @note Transform will be replaced by logo::Pass
+ */
+
+using Transform = logo::Pass;
+
+std::string transform_name(const Transform *);
+
+template <typename DERIVED> DERIVED *as(loco::Node *node) { return dynamic_cast<DERIVED *>(node); }
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_TRANSFORM_H__
diff --git a/compiler/moco-tf/src/Transform.test.cpp b/compiler/moco-tf/src/Transform.test.cpp
new file mode 100644
index 000000000..e029b54c4
--- /dev/null
+++ b/compiler/moco-tf/src/Transform.test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2019 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 "Transform.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(Transform, transform_name_over_unnamed_transform)
+{
+ struct SampleTransform final : public moco::tf::Transform
+ {
+ bool run(loco::Graph *) final { return false; }
+ };
+
+ SampleTransform sample_transform;
+
+ ASSERT_EQ(moco::tf::transform_name(&sample_transform), "(unknown)");
+}
+
+TEST(Transform, transform_name_over_named_transform)
+{
+ struct SampleTransform final : public moco::tf::Transform
+ {
+ const char *name(void) const final { return "sample"; }
+ bool run(loco::Graph *) final { return false; }
+ };
+
+ SampleTransform sample_transform;
+
+ ASSERT_EQ(moco::tf::transform_name(&sample_transform), "sample");
+}
diff --git a/compiler/moco-tf/src/Transforms.h b/compiler/moco-tf/src/Transforms.h
new file mode 100644
index 000000000..f14b81675
--- /dev/null
+++ b/compiler/moco-tf/src/Transforms.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_TRANSFORMS_H__
+#define __MOCO_TF_TRANSFORMS_H__
+
+#include "Transforms/ShapeInferencePass.h"
+#include "Transforms/TypeInferencePass.h"
+
+#include <logo/Passes.h>
+#include <moco/Pass/Passes.h>
+
+#endif // __MOCO_TF_TRANSFORMS_H__
diff --git a/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp
new file mode 100644
index 000000000..64ba9dfb1
--- /dev/null
+++ b/compiler/moco-tf/src/Transforms/ShapeInferencePass.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 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 "ShapeInferencePass.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Service/TFShapeInferenceRule.h>
+
+#include <loco.h>
+
+#include <loco/IR/CanonicalDialect.h>
+
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/ShapeInferenceRule.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpShapeInferenceRule.h>
+
+namespace moco
+{
+namespace tf
+{
+
+bool ShapeInferencePass::run(loco::Graph *graph)
+{
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ moco::TFShapeInferenceRule tf_rule;
+ locoex::COpShapeInferenceRule cop_rule; // rule for custop op
+
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(TFDialect::get(), &tf_rule)
+ .bind(locoex::COpDialect::get(), &cop_rule);
+
+ return loco::apply(&rules).to(graph);
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/ShapeInferencePass.h b/compiler/moco-tf/src/Transforms/ShapeInferencePass.h
new file mode 100644
index 000000000..f8be5f146
--- /dev/null
+++ b/compiler/moco-tf/src/Transforms/ShapeInferencePass.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_SHAPE_INFERENCE_PASS_H__
+#define __MOCO_TF_SHAPE_INFERENCE_PASS_H__
+
+#include "Transform.h"
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Run shape inference to the graph
+ */
+class ShapeInferencePass : public Transform
+{
+public:
+ const char *name(void) const final { return "ShapeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_SHAPE_INFERENCE_PASS_H__
diff --git a/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp
new file mode 100644
index 000000000..db6cf7521
--- /dev/null
+++ b/compiler/moco-tf/src/Transforms/TypeInferencePass.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2019 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 "TypeInferencePass.h"
+
+#include <moco/IR/TFDialect.h>
+
+#include <moco/Service/TFTypeInferenceRule.h>
+
+#include <loco.h>
+
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <locoex/COpDialect.h>
+#include <locoex/Service/COpTypeInference.h>
+
+namespace moco
+{
+namespace tf
+{
+
+bool TypeInferencePass::run(loco::Graph *graph)
+{
+ loco::CanonicalTypeInferenceRule canonical_rule;
+ moco::TFTypeInferenceRule tf_rule; // rule for TF dialect
+ locoex::COpTypeInferenceRule cop_rule; // rule for custop op
+
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(TFDialect::get(), &tf_rule)
+ .bind(locoex::COpDialect::get(), &cop_rule);
+
+ loco::apply(&rules).to(graph);
+
+ return false;
+}
+
+} // namespace tf
+} // namespace moco
diff --git a/compiler/moco-tf/src/Transforms/TypeInferencePass.h b/compiler/moco-tf/src/Transforms/TypeInferencePass.h
new file mode 100644
index 000000000..88a2f86f1
--- /dev/null
+++ b/compiler/moco-tf/src/Transforms/TypeInferencePass.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2019 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.
+ */
+
+#ifndef __MOCO_TF_TYPE_INFERENCE_PASS_H__
+#define __MOCO_TF_TYPE_INFERENCE_PASS_H__
+
+#include "Transform.h"
+
+#include <loco.h>
+
+namespace moco
+{
+namespace tf
+{
+
+/**
+ * @brief Run type inference to the graph
+ */
+class TypeInferencePass : public Transform
+{
+public:
+ const char *name(void) const final { return "TypeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph) override;
+};
+
+} // namespace tf
+} // namespace moco
+
+#endif // __MOCO_TF_TYPE_INFERENCE_PASS_H__