summaryrefslogtreecommitdiff
path: root/compiler/luci
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/luci')
-rw-r--r--compiler/luci/CMakeLists.txt10
-rw-r--r--compiler/luci/README.md3
-rw-r--r--compiler/luci/export/CMakeLists.txt29
-rw-r--r--compiler/luci/export/README.md3
-rw-r--r--compiler/luci/export/include/luci/CircleExporter.h64
-rw-r--r--compiler/luci/export/src/Check.h35
-rw-r--r--compiler/luci/export/src/CircleExporter.cpp64
-rw-r--r--compiler/luci/export/src/CircleExporterImpl.cpp266
-rw-r--r--compiler/luci/export/src/CircleExporterImpl.h82
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.cpp169
-rw-r--r--compiler/luci/export/src/CircleExporterUtils.h51
-rw-r--r--compiler/luci/export/src/CircleOperationExporter.cpp643
-rw-r--r--compiler/luci/export/src/CircleOperationExporter.h37
-rw-r--r--compiler/luci/export/src/CircleTensorExporter.cpp264
-rw-r--r--compiler/luci/export/src/CircleTensorExporter.h44
-rw-r--r--compiler/luci/export/src/Optimize.cpp48
-rw-r--r--compiler/luci/export/src/Optimize.h33
-rw-r--r--compiler/luci/export/src/ProgressReporter.cpp84
-rw-r--r--compiler/luci/export/src/ProgressReporter.h53
-rw-r--r--compiler/luci/export/src/SerializedData.h95
-rw-r--r--compiler/luci/import/CMakeLists.txt26
-rw-r--r--compiler/luci/import/README.md3
-rw-r--r--compiler/luci/import/include/luci/Import/CircleReader.h87
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilder.h56
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilderContext.h79
-rw-r--r--compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h85
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes.h48
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleConst.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleCos.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h36
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleExp.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMean.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleMul.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePack.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CirclePad.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleSub.h37
-rw-r--r--compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h37
-rw-r--r--compiler/luci/import/include/luci/Importer.h54
-rw-r--r--compiler/luci/import/src/CircleReader.cpp211
-rw-r--r--compiler/luci/import/src/GraphBuilder.cpp61
-rw-r--r--compiler/luci/import/src/GraphBuilderContext.cpp47
-rw-r--r--compiler/luci/import/src/GraphBuilderRegistry.cpp163
-rw-r--r--compiler/luci/import/src/Importer.cpp253
-rw-r--r--compiler/luci/import/src/Importer.test.cpp23
-rw-r--r--compiler/luci/import/src/Nodes/CircleAbs.cpp44
-rw-r--r--compiler/luci/import/src/Nodes/CircleAdd.cpp48
-rw-r--r--compiler/luci/import/src/Nodes/CircleArgMax.cpp48
-rw-r--r--compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp80
-rw-r--r--compiler/luci/import/src/Nodes/CircleConcatenation.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleConst.cpp110
-rw-r--r--compiler/luci/import/src/Nodes/CircleConv2D.cpp58
-rw-r--r--compiler/luci/import/src/Nodes/CircleCos.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp60
-rw-r--r--compiler/luci/import/src/Nodes/CircleDiv.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleEqual.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleExp.cpp59
-rw-r--r--compiler/luci/import/src/Nodes/CircleFullyConnected.cpp56
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogicalNot.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleLogicalOr.cpp55
-rw-r--r--compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp52
-rw-r--r--compiler/luci/import/src/Nodes/CircleMean.cpp46
-rw-r--r--compiler/luci/import/src/Nodes/CircleMul.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CirclePack.cpp61
-rw-r--r--compiler/luci/import/src/Nodes/CirclePad.cpp50
-rw-r--r--compiler/luci/import/src/Nodes/CircleRelu.cpp47
-rw-r--r--compiler/luci/import/src/Nodes/CircleReshape.cpp82
-rw-r--r--compiler/luci/import/src/Nodes/CircleRsqrt.cpp60
-rw-r--r--compiler/luci/import/src/Nodes/CircleSoftmax.cpp49
-rw-r--r--compiler/luci/import/src/Nodes/CircleSub.cpp51
-rw-r--r--compiler/luci/import/src/Nodes/CircleTranspose.cpp51
-rw-r--r--compiler/luci/lang/CMakeLists.txt22
-rw-r--r--compiler/luci/lang/README.md3
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrFilter.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h36
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrPadding.h33
-rw-r--r--compiler/luci/lang/include/luci/IR/AttrStride.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleDialect.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNode.h23
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeDecl.h68
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeImpl.h70
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h30
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h87
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodes.h73
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleNodes.lst52
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleOpcode.h32
-rw-r--r--compiler/luci/lang/include/luci/IR/CircleQuantParam.h36
-rw-r--r--compiler/luci/lang/include/luci/IR/LuciNodeMixins.h104
-rw-r--r--compiler/luci/lang/include/luci/IR/Module.h70
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h45
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h63
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h72
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h57
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h62
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h68
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h55
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h56
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h40
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h43
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h62
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h51
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h45
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h55
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h67
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h46
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h69
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h47
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h44
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h48
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h48
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h50
-rw-r--r--compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h64
-rw-r--r--compiler/luci/lang/include/luci/IR/VariadicArityNode.h77
-rw-r--r--compiler/luci/lang/src/Check.h35
-rw-r--r--compiler/luci/lang/src/CircleDialect.cpp88
-rw-r--r--compiler/luci/lang/src/CircleDialect.test.cpp34
-rw-r--r--compiler/luci/lang/src/CircleNode.cpp25
-rw-r--r--compiler/luci/lang/src/CircleNodes.cpp50
-rw-r--r--compiler/luci/lang/src/LuciNodeMixins.cpp18
-rw-r--r--compiler/luci/lang/src/Module.cpp46
-rw-r--r--compiler/luci/lang/src/Module.test.cpp73
-rw-r--r--compiler/luci/lang/src/Nodes/CircleAbs.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleAdd.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp33
-rw-r--r--compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp35
-rw-r--r--compiler/luci/lang/src/Nodes/CircleConst.cpp79
-rw-r--r--compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp33
-rw-r--r--compiler/luci/lang/src/Nodes/CircleCos.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp38
-rw-r--r--compiler/luci/lang/src/Nodes/CircleDiv.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleEqual.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleExp.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp34
-rw-r--r--compiler/luci/lang/src/Nodes/CircleGather.test.cpp33
-rw-r--r--compiler/luci/lang/src/Nodes/CircleInput.cpp38
-rw-r--r--compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp35
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp33
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleMul.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleOutput.cpp38
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePack.test.cpp35
-rw-r--r--compiler/luci/lang/src/Nodes/CirclePad.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRelu.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleReshape.test.cpp48
-rw-r--r--compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp31
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleSub.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp32
-rw-r--r--compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp33
-rw-r--r--compiler/luci/log/CMakeLists.txt9
-rw-r--r--compiler/luci/log/README.md3
-rw-r--r--compiler/luci/log/include/luci/Log.h75
-rw-r--r--compiler/luci/log/include/luci/LoggingContext.h35
-rw-r--r--compiler/luci/log/src/Log.cpp87
-rw-r--r--compiler/luci/log/src/LoggingContext.cpp41
-rw-r--r--compiler/luci/logex/CMakeLists.txt13
-rw-r--r--compiler/luci/logex/README.md3
-rw-r--r--compiler/luci/logex/include/luci/FormattedGraph.h56
-rw-r--r--compiler/luci/logex/include/luci/LogHelper.h36
-rw-r--r--compiler/luci/logex/src/FormattedGraph.cpp606
-rw-r--r--compiler/luci/logex/src/LogHelper.cpp29
-rw-r--r--compiler/luci/pass/CMakeLists.txt29
-rw-r--r--compiler/luci/pass/README.md3
-rw-r--r--compiler/luci/pass/include/luci/CircleOptimizer.h55
-rw-r--r--compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h40
-rw-r--r--compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h41
-rw-r--r--compiler/luci/pass/include/luci/Pass/TypeInferencePass.h42
-rw-r--r--compiler/luci/pass/src/CircleOptimizer.cpp96
-rw-r--r--compiler/luci/pass/src/FuseInstanceNormPass.cpp401
-rw-r--r--compiler/luci/pass/src/ProgressReporter.cpp84
-rw-r--r--compiler/luci/pass/src/ProgressReporter.h53
-rw-r--r--compiler/luci/pass/src/ShapeInferencePass.cpp44
-rw-r--r--compiler/luci/pass/src/TypeInferencePass.cpp42
-rw-r--r--compiler/luci/requires.cmake9
-rw-r--r--compiler/luci/service/CMakeLists.txt25
-rw-r--r--compiler/luci/service/README.md3
-rw-r--r--compiler/luci/service/include/luci/Service/CircleShapeInference.h41
-rw-r--r--compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h33
-rw-r--r--compiler/luci/service/include/luci/Service/CircleTypeInference.h42
-rw-r--r--compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h36
-rw-r--r--compiler/luci/service/include/luci/Service/ShapeDescription.h59
-rw-r--r--compiler/luci/service/include/luci/Service/Validate.h29
-rw-r--r--compiler/luci/service/src/Check.h35
-rw-r--r--compiler/luci/service/src/CircleShapeInference.cpp37
-rw-r--r--compiler/luci/service/src/CircleShapeInferenceRule.cpp907
-rw-r--r--compiler/luci/service/src/CircleShapeInferenceRule.test.cpp282
-rw-r--r--compiler/luci/service/src/CircleTypeInference.cpp78
-rw-r--r--compiler/luci/service/src/CircleTypeInferenceRule.cpp202
-rw-r--r--compiler/luci/service/src/CircleTypeInferenceRule.test.cpp57
-rw-r--r--compiler/luci/service/src/GraphBlock.h201
-rw-r--r--compiler/luci/service/src/GraphBlock.test.cpp246
-rw-r--r--compiler/luci/service/src/ShapeDescription.cpp139
-rw-r--r--compiler/luci/service/src/TestGraph.h315
-rw-r--r--compiler/luci/service/src/Validate.cpp109
-rw-r--r--compiler/luci/tester/CMakeLists.txt22
-rw-r--r--compiler/luci/tester/src/Model.cpp62
-rw-r--r--compiler/luci/tester/src/Model.h27
-rw-r--r--compiler/luci/tester/src/ReadTester.cpp92
-rw-r--r--compiler/luci/tester/src/WriteTester.cpp142
-rw-r--r--compiler/luci/tests/.gitignore1
-rw-r--r--compiler/luci/tests/CMakeLists.txt97
-rwxr-xr-xcompiler/luci/tests/readverify.sh53
-rw-r--r--compiler/luci/tests/test.lst91
-rwxr-xr-xcompiler/luci/tests/writeverify.sh53
240 files changed, 15569 insertions, 0 deletions
diff --git a/compiler/luci/CMakeLists.txt b/compiler/luci/CMakeLists.txt
new file mode 100644
index 000000000..387c22487
--- /dev/null
+++ b/compiler/luci/CMakeLists.txt
@@ -0,0 +1,10 @@
+add_subdirectory(log)
+add_subdirectory(lang)
+add_subdirectory(service)
+add_subdirectory(pass)
+add_subdirectory(logex)
+add_subdirectory(import)
+add_subdirectory(export)
+add_subdirectory(tester)
+
+add_subdirectory(tests)
diff --git a/compiler/luci/README.md b/compiler/luci/README.md
new file mode 100644
index 000000000..49c833121
--- /dev/null
+++ b/compiler/luci/README.md
@@ -0,0 +1,3 @@
+# luci
+
+_luci_ provides IR for TFLite/Circle and Graph from FlatBuffer.
diff --git a/compiler/luci/export/CMakeLists.txt b/compiler/luci/export/CMakeLists.txt
new file mode 100644
index 000000000..e32eca366
--- /dev/null
+++ b/compiler/luci/export/CMakeLists.txt
@@ -0,0 +1,29 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+# TODO enable tests
+#file(GLOB_RECURSE TESTS "src/*.test.cpp")
+#list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_export SHARED ${SOURCES})
+target_include_directories(luci_export PRIVATE src)
+target_include_directories(luci_export PUBLIC include)
+target_link_libraries(luci_export PRIVATE luci_lang)
+target_link_libraries(luci_export PRIVATE luci_service)
+target_link_libraries(luci_export PRIVATE luci_pass)
+target_link_libraries(luci_export PRIVATE mio_circle)
+target_link_libraries(luci_export PRIVATE luci_log)
+target_link_libraries(luci_export PRIVATE luci_logex)
+target_link_libraries(luci_export PRIVATE nncc_common)
+target_link_libraries(luci_export PRIVATE locop)
+target_link_libraries(luci_export PRIVATE oops)
+install(TARGETS luci_export DESTINATION lib)
+
+#if(NOT ENABLE_TEST)
+# return()
+#endif(NOT ENABLE_TEST)
+#
+#nnas_find_package(GTest REQUIRED)
+#
+#GTest_AddTest(luci_export_test ${TESTS})
+#target_include_directories(luci_export_test PRIVATE src)
+#target_link_libraries(luci_export_test luci_export)
+#target_link_libraries(luci_export_test oops)
diff --git a/compiler/luci/export/README.md b/compiler/luci/export/README.md
new file mode 100644
index 000000000..12b190a2f
--- /dev/null
+++ b/compiler/luci/export/README.md
@@ -0,0 +1,3 @@
+# luci-export
+
+_luci-export_ provides exporting _loco_ graph of Circle IR to Circle model file
diff --git a/compiler/luci/export/include/luci/CircleExporter.h b/compiler/luci/export/include/luci/CircleExporter.h
new file mode 100644
index 000000000..0584c623c
--- /dev/null
+++ b/compiler/luci/export/include/luci/CircleExporter.h
@@ -0,0 +1,64 @@
+/*
+ * 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 __LUCI_CIRCLEEXPORTER_H__
+#define __LUCI_CIRCLEEXPORTER_H__
+
+#include <luci/IR/Module.h>
+
+#include <loco.h>
+
+#include <memory>
+
+namespace luci
+{
+
+class CircleExporter
+{
+public:
+ // This contract class describes the interaction between a exporter and its client.
+ struct Contract
+ {
+ public:
+ virtual ~Contract() = default;
+
+ public: // Client -> Exporter
+ // Input Graph (to be exported)
+ // Exporter expects a loco graph that consists of Circle nodes
+ virtual loco::Graph *graph(void) const = 0;
+
+ // Input Module (to be exported)
+ // Exporter expects a luci module that consists of loco graphs
+ // TODO make this pure virtual
+ virtual luci::Module *module(void) const;
+
+ public: // Exporter -> Client
+ // Exporter calls store for export data
+ // Notice: Please DO NOT STORE ptr and size when implementing this in Client
+ virtual bool store(const char *ptr, const size_t size) const = 0;
+ };
+
+public:
+ explicit CircleExporter();
+
+public:
+ // invoke(...) returns false on failure.
+ bool invoke(Contract *) const;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLEEXPORTER_H__
diff --git a/compiler/luci/export/src/Check.h b/compiler/luci/export/src/Check.h
new file mode 100644
index 000000000..e05ec904a
--- /dev/null
+++ b/compiler/luci/export/src/Check.h
@@ -0,0 +1,35 @@
+/*
+ * 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 __CHECK_H__
+#define __CHECK_H__
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define LUCI_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/luci/export/src/CircleExporter.cpp b/compiler/luci/export/src/CircleExporter.cpp
new file mode 100644
index 000000000..125df7802
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporter.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "luci/CircleExporter.h"
+#include "luci/IR/Module.h"
+#include "CircleExporterImpl.h"
+
+#include <oops/InternalExn.h>
+
+#include <fstream>
+#include <memory>
+
+namespace luci
+{
+
+// TODO remove this
+Module *CircleExporter::Contract::module(void) const { return nullptr; }
+
+CircleExporter::CircleExporter()
+{
+ // NOTHING TO DO
+}
+
+bool CircleExporter::invoke(Contract *contract) const
+{
+ auto module = contract->module();
+ if (module != nullptr)
+ {
+ CircleExporterImpl impl(module);
+
+ const char *ptr = impl.getBufferPointer();
+ const size_t size = impl.getBufferSize();
+
+ // we just send one time
+ return contract->store(ptr, size);
+ }
+
+ auto graph = contract->graph();
+ if (graph == nullptr)
+ return false;
+
+ CircleExporterImpl impl(graph);
+
+ const char *ptr = impl.getBufferPointer();
+ const size_t size = impl.getBufferSize();
+
+ // we just send one time
+ return contract->store(ptr, size);
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleExporterImpl.cpp b/compiler/luci/export/src/CircleExporterImpl.cpp
new file mode 100644
index 000000000..81109ee62
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterImpl.cpp
@@ -0,0 +1,266 @@
+/*
+ * 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 "CircleExporterImpl.h"
+#include "Optimize.h"
+#include "CircleTensorExporter.h"
+#include "CircleOperationExporter.h"
+#include "CircleExporterUtils.h"
+
+#include <oops/InternalExn.h>
+#include <mio/circle/schema_generated.h>
+#include <flatbuffers/flatbuffers.h>
+
+#include <cassert>
+#include <unordered_map>
+#include <string>
+#include <stdexcept>
+
+namespace
+{
+
+luci::CircleInput *input_node(loco::Graph *g, const loco::GraphInputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto pull = dynamic_cast<luci::CircleInput *>(g->nodes()->at(n)))
+ {
+ if (pull->indexed() && pull->index() == index)
+ {
+ return pull;
+ }
+ }
+ }
+ return nullptr;
+}
+
+luci::CircleOutput *output_node(loco::Graph *g, const loco::GraphOutputIndex &index)
+{
+ for (uint32_t n = 0; n < g->nodes()->size(); ++n)
+ {
+ if (auto push = dynamic_cast<luci::CircleOutput *>(g->nodes()->at(n)))
+ {
+ if (push->indexed() && push->index() == index)
+ {
+ return push;
+ }
+ }
+ }
+ return nullptr;
+}
+
+void registerGraphInputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->inputs()->size(); ++n)
+ {
+ auto node = input_node(graph, n);
+ assert(node != nullptr);
+ ctx._inputs.push_back(luci::get_tensor_index(node));
+ }
+}
+
+void registerGraphOutputTensors(loco::Graph *graph, luci::SubGraphContext &ctx)
+{
+ for (uint32_t n = 0; n < graph->outputs()->size(); ++n)
+ {
+ auto push = output_node(graph, n);
+ assert(push != nullptr);
+ auto node = push->from();
+ assert(node != nullptr);
+ ctx._outputs.push_back(luci::get_tensor_index(node));
+ }
+}
+
+} // namespace
+
+namespace
+{
+
+using namespace circle;
+using namespace flatbuffers;
+
+Offset<Vector<Offset<OperatorCode>>>
+encodeOperatorCodes(FlatBufferBuilder &builder, std::unordered_map<luci::OpCode, uint32_t> &opcodes,
+ std::unordered_map<luci::OpCode, std::string> &custom_opcodes)
+{
+ std::vector<Offset<OperatorCode>> operator_codes_vec(opcodes.size());
+ for (auto it : opcodes)
+ {
+ uint32_t idx = it.second;
+ if (it.first.opcode != BuiltinOperator_CUSTOM)
+ {
+ operator_codes_vec[idx] = CreateOperatorCode(builder, it.first.opcode);
+ }
+ else // custom op
+ {
+ auto opCode = it.first;
+ auto custom_code = custom_opcodes.find(opCode);
+ if (custom_code == custom_opcodes.end())
+ INTERNAL_EXN("Cannot find code for customop even though opcode is BuiltinOperator_CUSTOM");
+
+ operator_codes_vec[idx] =
+ CreateOperatorCode(builder, it.first.opcode, builder.CreateString(custom_code->second));
+ }
+ }
+ return builder.CreateVector(operator_codes_vec);
+}
+
+} // namespace
+
+namespace luci
+{
+
+using namespace circle;
+using namespace flatbuffers;
+
+CircleExporterImpl::CircleExporterImpl(loco::Graph *graph) { exportGraph(graph); }
+CircleExporterImpl::CircleExporterImpl(Module *module) { exportModule(module); }
+
+::flatbuffers::Offset<::circle::SubGraph>
+CircleExporterImpl::exportSubgraph(SerializedGraphData &gd)
+{
+ auto tensors = _builder.CreateVector(gd._tensors);
+ auto inputs = _builder.CreateVector(gd._inputs);
+ auto outputs = _builder.CreateVector(gd._outputs);
+ auto operators = _builder.CreateVector(gd._operators);
+ auto df = gd._data_format;
+ auto subgraph = CreateSubGraph(_builder, tensors, inputs, outputs, operators, df);
+ return subgraph;
+}
+
+void CircleExporterImpl::exportGraph(loco::Graph *graph)
+{
+ // do graph optimization
+ optimize(graph);
+
+ _builder.Clear();
+
+ SerializedModelData md;
+ SerializedGraphData gd;
+
+ // This version is taken from comment in fbs
+ constexpr uint32_t version = 0;
+
+ // TODO set this value properly
+ gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
+
+ // prepare model data
+ prepareModelData(_builder, md);
+
+ // parse graph into SerializedModelData structure
+ exportOpDefinedTensors(graph, _builder, md, gd);
+
+ // NOTE Invoke these register functions only after each node is annotated with its tensor_index
+ registerGraphInputTensors(graph, gd);
+ registerGraphOutputTensors(graph, gd);
+
+ exportNodes(graph, _builder, md, gd);
+
+ // encode operator codes
+ auto operator_codes =
+ encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes);
+
+ // Subgraphs
+ Offset<SubGraph> subgraph = exportSubgraph(gd);
+ auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph});
+
+ // Description
+ std::string description_str = "nnpackage";
+ auto description = _builder.CreateString(description_str);
+
+ // create array of buffers
+ auto buffers = _builder.CreateVector(md._buffers);
+
+ // empty metadata
+ std::vector<int> metadata_buffer_vec;
+ auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
+
+ // Model
+ auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
+ buffers, metadata_buffer);
+ FinishModelBuffer(_builder, model_offset);
+}
+
+void CircleExporterImpl::exportModule(Module *module)
+{
+ assert(module->size() > 0);
+ // do graph optimization
+
+ SerializedModelData md;
+
+ _builder.Clear();
+
+ std::vector<flatbuffers::Offset<circle::SubGraph>> subgraph_vec;
+
+ for (size_t g = 0; g < module->size(); ++g)
+ {
+ auto graph = module->graph(g);
+
+ optimize(graph);
+
+ SerializedGraphData gd;
+
+ // TODO set this value properly
+ gd._data_format = circle::DataFormat::DataFormat_CHANNELS_LAST;
+
+ // parse graph into SerializedModelData structure
+ exportOpDefinedTensors(graph, _builder, md, gd);
+
+ // NOTE Invoke these register functions only after each node is annotated with its tensor_index
+ registerGraphInputTensors(graph, gd);
+ registerGraphOutputTensors(graph, gd);
+
+ exportNodes(graph, _builder, md, gd);
+
+ // Subgraphs
+ Offset<SubGraph> subgraph = exportSubgraph(gd);
+ subgraph_vec.push_back(subgraph);
+ }
+
+ auto subgraphs = _builder.CreateVector(std::vector<Offset<SubGraph>>{subgraph_vec});
+
+ // encode operator codes
+ auto operator_codes =
+ encodeOperatorCodes(_builder, md._operator_codes, md._custom_operator_codes);
+
+ // Description
+ std::string description_str = "nnpackage";
+ auto description = _builder.CreateString(description_str);
+
+ // create array of buffers
+ auto buffers = _builder.CreateVector(md._buffers);
+
+ // empty metadata
+ std::vector<int> metadata_buffer_vec;
+ auto metadata_buffer = _builder.CreateVector(metadata_buffer_vec);
+
+ // This version is taken from comment in fbs
+ constexpr uint32_t version = 0;
+
+ // Model
+ auto model_offset = CreateModel(_builder, version, operator_codes, subgraphs, description,
+ buffers, metadata_buffer);
+ FinishModelBuffer(_builder, model_offset);
+}
+
+const char *CircleExporterImpl::getBufferPointer() const
+{
+ return reinterpret_cast<const char *>(_builder.GetBufferPointer());
+}
+
+size_t CircleExporterImpl::getBufferSize() const { return _builder.GetSize(); }
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleExporterImpl.h b/compiler/luci/export/src/CircleExporterImpl.h
new file mode 100644
index 000000000..e5d5b5a00
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterImpl.h
@@ -0,0 +1,82 @@
+/*
+ * 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 __CIRCLE_EXPORTER_IMPL_H__
+#define __CIRCLE_EXPORTER_IMPL_H__
+
+#include "luci/CircleExporter.h"
+#include "luci/IR/Module.h"
+
+#include "SerializedData.h"
+
+#include "SerializedData.h"
+
+#include <mio/circle/schema_generated.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+/**
+ * internal implementation of interface exporter class
+ */
+class CircleExporterImpl
+{
+public:
+ CircleExporterImpl() = delete;
+ ~CircleExporterImpl() = default;
+
+ explicit CircleExporterImpl(loco::Graph *graph);
+ explicit CircleExporterImpl(Module *module);
+
+ /**
+ * @return pointer to buffer with serialized graph
+ */
+ const char *getBufferPointer() const;
+
+ /**
+ * @return size of buffer with serialized graph
+ */
+ size_t getBufferSize() const;
+
+private:
+ /**
+ * @brief create Subgraph using data stored in SerializedGraphData
+ * @param gd information about serializer parts of model
+ * @return offset in buffer corresponding to serialized subgraph
+ */
+ flatbuffers::Offset<circle::SubGraph> exportSubgraph(SerializedGraphData &gd);
+
+ /**
+ * @brief root function that writes graph into internal buffer
+ * @param graph
+ */
+ void exportGraph(loco::Graph *graph);
+
+ /**
+ * @brief root function that writes Module into internal buffer
+ * @param module
+ */
+ void exportModule(Module *module);
+
+private:
+ flatbuffers::FlatBufferBuilder _builder;
+};
+
+} // namespace luci
+
+#endif // __CIRCLE_EXPORTER_IMPL_H__
diff --git a/compiler/luci/export/src/CircleExporterUtils.cpp b/compiler/luci/export/src/CircleExporterUtils.cpp
new file mode 100644
index 000000000..1272facb2
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterUtils.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 "CircleExporterUtils.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+#include <memory>
+
+namespace luci
+{
+
+circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func)
+{
+ switch (func)
+ {
+ case luci::FusedActFunc::NONE:
+ return circle::ActivationFunctionType_NONE;
+ case luci::FusedActFunc::RELU:
+ return circle::ActivationFunctionType_RELU;
+ case luci::FusedActFunc::RELU_N1_TO_1:
+ return circle::ActivationFunctionType_RELU_N1_TO_1;
+ case luci::FusedActFunc::RELU6:
+ return circle::ActivationFunctionType_RELU6;
+ default:
+ INTERNAL_EXN_V("trying to convert unsupported luci::FusedActFunc", oops::to_uint32(func));
+ }
+}
+
+circle::TensorType to_circle_tensortype(loco::DataType type)
+{
+ switch (type)
+ {
+ case loco::DataType::U8:
+ return circle::TensorType_UINT8;
+
+ case loco::DataType::S8:
+ return circle::TensorType_INT8;
+ case loco::DataType::S16:
+ return circle::TensorType_INT16;
+ case loco::DataType::S32:
+ return circle::TensorType_INT32;
+ case loco::DataType::S64:
+ return circle::TensorType_INT64;
+
+ case loco::DataType::FLOAT16:
+ return circle::TensorType_FLOAT16;
+ case loco::DataType::FLOAT32:
+ return circle::TensorType_FLOAT32;
+
+ case loco::DataType::BOOL:
+ return circle::TensorType_BOOL;
+
+ default:
+ INTERNAL_EXN_V("failed to convert unsupported loco::DataType", oops::to_uint32(type));
+ }
+}
+
+} // namespace luci
+
+namespace luci
+{
+
+uint32_t SerializedModelData::registerBuiltinOpcode(circle::BuiltinOperator builtin_code)
+{
+ auto it = _operator_codes.find(OpCode{builtin_code});
+ if (it != _operator_codes.end())
+ {
+ return it->second;
+ }
+ auto idx = static_cast<uint32_t>(_operator_codes.size());
+ _operator_codes.emplace(OpCode{builtin_code}, idx);
+ return idx;
+}
+
+uint32_t SerializedModelData::registerCustomOpcode(const std::string &custom_op)
+{
+ circle::BuiltinOperator custom_code = circle::BuiltinOperator_CUSTOM;
+ auto idx = registerBuiltinOpcode(custom_code);
+ _custom_operator_codes.emplace(OpCode{custom_code}, custom_op);
+ return idx;
+}
+
+circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm)
+{
+ // VALID padding
+ if (pad->top() == 0 && pad->bottom() == 0 && pad->left() == 0 && pad->right() == 0)
+ return circle::Padding_VALID;
+
+ // SAME padding
+ //
+ // For same padding, by definition, following equation should hold:
+ // O = floor((I - 1) / S) + 1
+ // where input size I, output size O, stride S
+ //
+ // NOTE input and output 'feature' map are shape of NHWC
+ bool same_padding_criterion_1 =
+ (static_cast<uint32_t>(ofm._dims[1]) == (ifm._dims[1] - 1) / stride->vertical() + 1) &&
+ (static_cast<uint32_t>(ofm._dims[2]) == (ifm._dims[2] - 1) / stride->horizontal() + 1);
+
+ // For same padding, rear padding is same or bigger than front padding by at most 1
+ bool same_padding_criterion_2 =
+ (pad->top() <= pad->bottom()) && (pad->bottom() <= pad->top() + 1) &&
+ (pad->left() <= pad->right()) && (pad->right() <= pad->left() + 1);
+
+ if (same_padding_criterion_1 && same_padding_criterion_2)
+ return circle::Padding_SAME;
+
+ INTERNAL_EXN("Unsupported padding criteria");
+}
+
+circle::Padding getOpPadding(const luci::Padding pad)
+{
+ if (pad == luci::Padding::VALID)
+ return circle::Padding_VALID;
+ if (pad == luci::Padding::SAME)
+ return circle::Padding_SAME;
+
+ INTERNAL_EXN_V("Unsupported luci::Padding", oops::to_uint32(pad));
+}
+
+namespace
+{
+
+class CircleTensorIndexAnnotation final : public loco::NodeAnnotation
+{
+public:
+ CircleTensorIndexAnnotation(const CircleTensorIndex &index) : _index{index}
+ {
+ // DO NOTHING
+ }
+
+public:
+ const CircleTensorIndex &index(void) const { return _index; }
+
+private:
+ CircleTensorIndex _index;
+};
+
+} // namespace
+
+void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id)
+{
+ assert(node->annot<CircleTensorIndexAnnotation>() == nullptr);
+ node->annot(std::make_unique<CircleTensorIndexAnnotation>(tensor_id));
+}
+
+CircleTensorIndex get_tensor_index(loco::Node *node)
+{
+ assert(node->annot<CircleTensorIndexAnnotation>() != nullptr);
+ return node->annot<CircleTensorIndexAnnotation>()->index();
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleExporterUtils.h b/compiler/luci/export/src/CircleExporterUtils.h
new file mode 100644
index 000000000..6b970fd3c
--- /dev/null
+++ b/compiler/luci/export/src/CircleExporterUtils.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __CIRCLE_EXPORTER_UTILS_H__
+#define __CIRCLE_EXPORTER_UTILS_H__
+
+#include "SerializedData.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/Service/ShapeDescription.h>
+
+#include <loco.h>
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+circle::ActivationFunctionType to_circle_actfunc(luci::FusedActFunc func);
+circle::TensorType to_circle_tensortype(loco::DataType type);
+
+} // namespace luci
+
+namespace luci
+{
+
+circle::Padding getOpPadding(const loco::Padding2D *pad, const loco::Stride<2> *stride,
+ const ShapeDescription &ifm, const ShapeDescription &ofm);
+circle::Padding getOpPadding(const luci::Padding pad);
+
+using CircleTensorIndex = int32_t;
+
+void set_tensor_index(loco::Node *node, const CircleTensorIndex &tensor_id);
+CircleTensorIndex get_tensor_index(loco::Node *node);
+
+} // namespace luci
+
+#endif // __CIRCLE_EXPORTER_UTILS_H__
diff --git a/compiler/luci/export/src/CircleOperationExporter.cpp b/compiler/luci/export/src/CircleOperationExporter.cpp
new file mode 100644
index 000000000..ad9c7fd4b
--- /dev/null
+++ b/compiler/luci/export/src/CircleOperationExporter.cpp
@@ -0,0 +1,643 @@
+/*
+ * 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 "CircleOperationExporter.h"
+#include "CircleExporterUtils.h"
+#include "Check.h"
+
+#include <luci/IR/CircleNode.h>
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Service/CircleShapeInference.h>
+
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <oops/InternalExn.h>
+
+#include <flatbuffers/flexbuffers.h>
+
+using namespace flatbuffers;
+using namespace circle;
+
+namespace
+{
+
+using namespace luci;
+
+class OperationExporter final : public luci::CircleNodeMutableVisitor<void>,
+ public loco::CanonicalNodeMutableVisitor<void>
+{
+public:
+ OperationExporter(FlatBufferBuilder &fbb, SerializedModelData &m, SerializedGraphData &g)
+ : builder{fbb}, md{m}, gd{g}
+ {
+ // DO NOTHING
+ }
+
+public:
+ void visit(luci::CircleAbs *) final;
+ void visit(luci::CircleAdd *) final;
+ void visit(luci::CircleArgMax *) final;
+ void visit(luci::CircleAveragePool2D *) final;
+ void visit(luci::CircleBatchToSpaceND *) final;
+ void visit(luci::CircleConcatenation *) final;
+ void visit(luci::CircleConst *) final{/* skip, everything is done in exportOpDefinedTensors */};
+ void visit(luci::CircleConv2D *) final;
+ void visit(luci::CircleCos *) final;
+ void visit(luci::CircleDepthwiseConv2D *) final;
+ void visit(luci::CircleDiv *) final;
+ void visit(luci::CircleExp *) final;
+ void visit(luci::CircleEqual *) final;
+ void visit(luci::CircleFullyConnected *) final;
+ void visit(luci::CircleLogicalNot *) final;
+ void visit(luci::CircleLogicalOr *) final;
+ void visit(luci::CircleMaximum *) final;
+ void visit(luci::CircleMaxPool2D *) final;
+ void visit(luci::CircleMean *) final;
+ void visit(luci::CircleMul *) final;
+ void visit(luci::CirclePack *) final;
+ void visit(luci::CirclePad *) final;
+ void visit(luci::CircleRelu *) final;
+ void visit(luci::CircleRelu6 *) final;
+ void visit(luci::CircleReshape *) final;
+ void visit(luci::CircleRsqrt *) final;
+ void visit(luci::CircleSoftmax *) final;
+ void visit(luci::CircleSqrt *) final;
+ void visit(luci::CircleSquaredDifference *) final;
+ void visit(luci::CircleSub *) final;
+ // TODO CircleTanh
+ void visit(luci::CircleTranspose *) final;
+ void visit(luci::CircleTransposeConv *) final;
+ // Circle only
+ void visit(luci::CircleInstanceNorm *) final;
+ // Virtual
+ void visit(luci::CircleInput *) final {}
+ void visit(luci::CircleOutput *) final {}
+
+private:
+ /**
+ * @brief Exports CircleMaxPool2D or CircleAveragePool2D
+ *
+ * @note CirclePool2D should be one of CircleMaxPool2D or CircleAveragePool2D
+ */
+ template <class CirclePool2D>
+ void export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op);
+
+private:
+ FlatBufferBuilder &builder;
+ SerializedModelData &md;
+ SerializedGraphData &gd;
+};
+
+template <class CirclePool2D>
+void OperationExporter::export_pool_2d(CirclePool2D *node, circle::BuiltinOperator builtin_op)
+{
+ LUCI_ASSERT(builtin_op == circle::BuiltinOperator_MAX_POOL_2D ||
+ builtin_op == circle::BuiltinOperator_AVERAGE_POOL_2D,
+ "Should be MaxPool or AvgPool");
+ LUCI_ASSERT(node->padding() != luci::Padding::UNDEFINED, "Padding is not set");
+
+ uint32_t op_idx = md.registerBuiltinOpcode(builtin_op);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->value())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ circle::Padding padding = getOpPadding(node->padding());
+
+ auto options = CreatePool2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
+ node->filter()->w(), node->filter()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Pool2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleAbs *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ABS);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAbsOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AbsOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleAdd *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ADD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAddOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_AddOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleArgMax *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_ARG_MAX);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->dimension())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateArgMaxOptions(builder, to_circle_tensortype(node->output_type()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ArgMaxOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleAveragePool2D *node)
+{
+ export_pool_2d<luci::CircleAveragePool2D>(node, circle::BuiltinOperator_AVERAGE_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleConcatenation *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONCATENATION);
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->values(i)));
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateConcatenationOptions(builder, node->axis(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ConcatenationOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleBatchToSpaceND *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_BATCH_TO_SPACE_ND);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->block_shape()),
+ get_tensor_index(node->crops())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateBatchToSpaceNDOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_BatchToSpaceNDOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleConv2D *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_CONV_2D);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(node->padding());
+ auto options = CreateConv2DOptions(builder, padding, node->stride()->w(), node->stride()->h(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+
+ // Make CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_Conv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleCos *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_COS);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateCosOptions(builder);
+
+ // Make COS operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_CosOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleDepthwiseConv2D *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DEPTHWISE_CONV_2D);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->filter()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(node->padding());
+ auto options = CreateDepthwiseConv2DOptions(builder, padding, node->stride()->w(),
+ node->stride()->h(), node->depthMultiplier(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+
+ // Make DEPTHWISE_CONV_2D operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_DepthwiseConv2DOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleDiv *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_DIV);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateDivOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_DivOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleExp *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_EXP);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateAbsOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ExpOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleFullyConnected *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_FULLY_CONNECTED);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->weights()),
+ get_tensor_index(node->bias())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options =
+ CreateFullyConnectedOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+
+ // Make FULLY_CONNECTED operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_FullyConnectedOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleLogicalNot *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_NOT);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateLogicalNotOptions(builder);
+
+ // Make LOGICAL_NOT operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_LogicalNotOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleLogicalOr *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_LOGICAL_OR);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateLogicalOrOptions(builder);
+
+ // Make LOGICAL_OR operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_LogicalOrOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleMaximum *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MAXIMUM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMaximumMinimumOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_MaximumMinimumOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleMaxPool2D *node)
+{
+ export_pool_2d<luci::CircleMaxPool2D>(node, circle::BuiltinOperator_MAX_POOL_2D);
+}
+
+void OperationExporter::visit(luci::CircleMean *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MEAN);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->reduction_indices())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateReducerOptions(builder, node->keep_dims());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReducerOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleMul *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_MUL);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateMulOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_MulOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CirclePack *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_PACK);
+ std::vector<int32_t> inputs_vec;
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+
+ for (uint32_t i = 0; i < node->values_count(); ++i)
+ inputs_vec.push_back(get_tensor_index(node->values(i)));
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreatePackOptions(builder, node->values_count(), node->axis());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_PackOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CirclePad *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_PAD);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()),
+ get_tensor_index(node->paddings())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreatePadOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_PadOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleRelu *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RELU);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleRelu6 *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RELU6);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->features())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleReshape *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RESHAPE);
+
+ // Create inputs and outputs.
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->tensor()),
+ get_tensor_index(node->shape())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+
+ // Create options.
+ auto new_shape = builder.CreateVector<int32_t>(
+ node->newShape()->rank(), [node](size_t i) { return node->newShape()->dim(i); });
+ auto options = CreateReshapeOptions(builder, new_shape);
+
+ // Create the operator.
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_ReshapeOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleRsqrt *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_RSQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleSoftmax *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SOFTMAX);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->logits())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSoftmaxOptions(builder, node->beta());
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SoftmaxOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleSqrt *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SQRT);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs);
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleSquaredDifference *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SQUARED_DIFFERENCE);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSquaredDifferenceOptions(builder);
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SquaredDifferenceOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleSub *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_SUB);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateSubOptions(builder, to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_SubOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+// TODO CircleTanh
+
+void OperationExporter::visit(luci::CircleTranspose *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->arg(0)), get_tensor_index(node->arg(1))};
+ std::vector<int32_t> outputs_vec{get_tensor_index(node)};
+
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateTransposeOptions(builder);
+
+ auto op_offset =
+ CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions::BuiltinOptions_TransposeOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleTransposeConv *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_TRANSPOSE_CONV);
+
+ // Make input, output and options for operator
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->inputSizes()),
+ get_tensor_index(node->filter()),
+ get_tensor_index(node->outBackprop())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ circle::Padding padding = getOpPadding(node->padding());
+ auto options =
+ CreateTransposeConvOptions(builder, padding, node->stride()->w(), node->stride()->h());
+
+ // Make TRANSPOSE_CONV operator
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_TransposeConvOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleInstanceNorm *node)
+{
+ uint32_t op_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_INSTANCE_NORM);
+ std::vector<int32_t> inputs_vec{get_tensor_index(node->input()), get_tensor_index(node->gamma()),
+ get_tensor_index(node->beta())};
+ std::vector<int32_t> outputs_vec{get_tensor_index(static_cast<loco::Node *>(node))};
+ auto inputs = builder.CreateVector(inputs_vec);
+ auto outputs = builder.CreateVector(outputs_vec);
+ auto options = CreateInstanceNormOptions(builder, node->epsilon(),
+ to_circle_actfunc(node->fusedActivationFunction()));
+ auto op_offset = CreateOperator(builder, op_idx, inputs, outputs,
+ circle::BuiltinOptions_InstanceNormOptions, options.Union());
+ gd._operators.push_back(op_offset);
+}
+
+void OperationExporter::visit(luci::CircleEqual *node)
+{
+ uint32_t opcode_idx = md.registerBuiltinOpcode(circle::BuiltinOperator_EQUAL);
+ std::vector<int32_t> inputs{get_tensor_index(node->x()), get_tensor_index(node->y())};
+ std::vector<int32_t> outputs{get_tensor_index(node)};
+
+ auto fb_inputs = builder.CreateVector(inputs);
+ auto fb_outputs = builder.CreateVector(outputs);
+
+ auto options = CreateEqualOptions(builder);
+
+ auto op_offset = CreateOperator(builder, opcode_idx, fb_inputs, fb_outputs,
+ circle::BuiltinOptions_EqualOptions, options.Union());
+
+ gd._operators.push_back(op_offset);
+}
+
+void exportNode(loco::Node *node, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd)
+{
+ // TODO Use explicit tagging to prevent possible mistake
+ auto isNoOp = [](loco::Node *node) {
+ // If there is only one input and the TensorIndex for the input is same
+ // as the TensorIndex of the output then this node is just a dummy node
+ if (node->arity() == 1)
+ {
+ assert(node->arg(0) != nullptr);
+ return get_tensor_index(node) == get_tensor_index(node->arg(0));
+ }
+ return false;
+ };
+
+ if (isNoOp(node))
+ {
+ // Skip if a given node is marked as NoOp (op with no effect) before
+ return;
+ }
+
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ OperationExporter exporter{builder, md, gd};
+ circle_node->accept(&exporter);
+ }
+ else
+ {
+ INTERNAL_EXN("Node with unsupported dialect found");
+ }
+}
+
+} // namespace
+
+namespace luci
+{
+
+void exportNodes(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd)
+{
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ exportNode(node, builder, md, gd);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleOperationExporter.h b/compiler/luci/export/src/CircleOperationExporter.h
new file mode 100644
index 000000000..de6abfc54
--- /dev/null
+++ b/compiler/luci/export/src/CircleOperationExporter.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __CIRCLE_OPERATION_EXPORTER_H__
+#define __CIRCLE_OPERATION_EXPORTER_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+namespace luci
+{
+
+/**
+ * @brief create Operators corresponding to model nodes
+ * @param nodes container with nodes
+ * @param gd information about serializer parts of model
+ */
+void exportNodes(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd);
+
+} // namespace luci
+
+#endif // __CIRCLE_OPERATION_EXPORTER_H__
diff --git a/compiler/luci/export/src/CircleTensorExporter.cpp b/compiler/luci/export/src/CircleTensorExporter.cpp
new file mode 100644
index 000000000..ef9b9d7d9
--- /dev/null
+++ b/compiler/luci/export/src/CircleTensorExporter.cpp
@@ -0,0 +1,264 @@
+/*
+ * 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 "CircleTensorExporter.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Service/CircleTypeInference.h>
+#include <luci/Service/CircleShapeInference.h>
+#include <luci/Log.h>
+
+#include <loco/IR/Algorithm.h>
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/IR/DataTypeTraits.h>
+#include <oops/InternalExn.h>
+
+using namespace circle;
+using namespace flatbuffers;
+
+namespace
+{
+
+using namespace luci;
+
+class CircleTensoInfo
+{
+public:
+ CircleTensoInfo() = default;
+
+public:
+ void name(const std::string &name) { _name = name; }
+ const std::string &name(void) const { return _name; }
+
+public:
+ const circle::TensorType &dtype(void) const { return _dtype; }
+ void dtype(const circle::TensorType &dtype) { _dtype = dtype; }
+
+ const ShapeDescription &shape(void) const { return _shape; }
+ void shape(const ShapeDescription &shape) { _shape = shape; }
+
+public:
+ luci::CircleConst *content(void) const { return _content; }
+ void content(luci::CircleConst *c) { _content = c; }
+
+ luci::CircleQuantParam *quantparam(void) const { return _quantparam; }
+ void quantparam(luci::CircleQuantParam *qp) { _quantparam = qp; }
+
+private:
+ std::string _name;
+
+ circle::TensorType _dtype;
+ ShapeDescription _shape;
+
+ luci::CircleConst *_content = nullptr;
+ luci::CircleQuantParam *_quantparam = nullptr;
+};
+
+using CircleTensorContext = std::vector<CircleTensoInfo>;
+
+struct NoOpDetector final : public luci::CircleNodeMutableVisitor<bool>
+{
+ // Input is Virtual but does produce a Tensor
+ // Output is Virtual that does not produce any Tensor
+ bool visit(luci::CircleOutput *) final { return true; }
+
+ // Return false by default
+ bool visit(luci::CircleNode *) final { return false; }
+};
+
+void allocateCircleTensor(CircleNode *node, CircleTensorContext &ctx)
+{
+ LOGGER(l);
+
+ auto isNoOp = [](loco::Node *node) {
+ if (auto circle_node = dynamic_cast<luci::CircleNode *>(node))
+ {
+ NoOpDetector d;
+ return circle_node->accept(&d);
+ }
+ return false;
+ };
+
+ if (isNoOp(node))
+ {
+ set_tensor_index(node, get_tensor_index(node->arg(0)));
+ return;
+ }
+
+ auto tensor_index = static_cast<CircleTensorIndex>(ctx.size());
+ // TODO Use Graph-level metadata for Input & Output
+ // auto tensor_name = "t_" + std::to_string(tensor_index);
+ std::string tensor_name = node->name();
+ if (tensor_name.empty())
+ tensor_name = "t_" + std::to_string(tensor_index);
+ INFO(l) << "[luci] Tensor for " << tensor_name << ": " << tensor_index << std::endl;
+
+ CircleTensoInfo tensor_info;
+
+ tensor_info.name(tensor_name);
+ tensor_info.dtype(TypeInference::get(node));
+ tensor_info.shape(ShapeInference::get(node));
+
+ tensor_info.content(dynamic_cast<luci::CircleConst *>(node));
+ tensor_info.quantparam(node->quantparam());
+
+ set_tensor_index(node, tensor_index);
+
+ ctx.emplace_back(tensor_info);
+}
+
+} // namespace
+
+namespace
+{
+
+flatbuffers::Offset<Vector<int32_t>> encodeShape(FlatBufferBuilder &builder,
+ const ShapeDescription &shape)
+{
+ assert(shape._rank_known && "unknown number of dimensions is not supported");
+ return builder.CreateVector(shape._dims);
+}
+
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder)
+{
+ return CreateBuffer(builder);
+}
+
+template <typename NodeT>
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, NodeT *)
+{
+ return CreateBuffer(builder);
+}
+
+template <loco::DataType DT>
+flatbuffers::Offset<circle::Buffer> encodeOpBufferByDType(FlatBufferBuilder &builder,
+ luci::CircleConst *c)
+{
+ using NativeType = typename loco::DataTypeImpl<DT>::Type;
+
+ std::vector<NativeType> raw_data;
+ const uint32_t size = c->size<DT>();
+ raw_data.reserve(size);
+ for (uint32_t i = 0; i < size; ++i)
+ {
+ raw_data.push_back(c->at<DT>(i));
+ }
+ const size_t raw_size = size * sizeof(NativeType);
+ auto array_offset = builder.CreateVector(reinterpret_cast<uint8_t *>(raw_data.data()), raw_size);
+ return CreateBuffer(builder, array_offset);
+}
+
+template <>
+flatbuffers::Offset<circle::Buffer> encodeOpBuffer(FlatBufferBuilder &builder, luci::CircleConst *c)
+{
+ // TODO use switch
+ if (c->dtype() == loco::DataType::FLOAT32)
+ {
+ return encodeOpBufferByDType<loco::DataType::FLOAT32>(builder, c);
+ }
+ else if (c->dtype() == loco::DataType::S32)
+ {
+ return encodeOpBufferByDType<loco::DataType::S32>(builder, c);
+ }
+ else if (c->dtype() == loco::DataType::U8)
+ {
+ return encodeOpBufferByDType<loco::DataType::U8>(builder, c);
+ }
+
+ INTERNAL_EXN_V("Unsupported datatype", oops::to_uint32(c->dtype()));
+}
+
+flatbuffers::Offset<circle::QuantizationParameters>
+encodeQuantizationParameters(FlatBufferBuilder &builder, luci::CircleQuantParam *quantparam)
+{
+ if (quantparam == nullptr)
+ return 0;
+
+ flatbuffers::Offset<flatbuffers::Vector<float>> min;
+ flatbuffers::Offset<flatbuffers::Vector<float>> max;
+ flatbuffers::Offset<flatbuffers::Vector<float>> scale;
+ flatbuffers::Offset<flatbuffers::Vector<int64_t>> zero_point;
+ if (quantparam->min.size() && quantparam->max.size())
+ {
+ min = builder.CreateVector(quantparam->min);
+ max = builder.CreateVector(quantparam->max);
+ }
+ if (quantparam->scale.size() && quantparam->zerop.size())
+ {
+ scale = builder.CreateVector(quantparam->scale);
+ zero_point = builder.CreateVector(quantparam->zerop);
+ }
+ return circle::CreateQuantizationParameters(builder, min, max, scale, zero_point);
+}
+
+void exportOpDefinedTensor(const CircleTensoInfo &info, FlatBufferBuilder &builder,
+ SerializedModelData &md, SerializedGraphData &gd)
+{
+ // Create and register output tensor shape
+ auto shape_offset = encodeShape(builder, info.shape());
+
+ // encode and register output tensor buffer
+ auto buffer =
+ info.content() == nullptr ? encodeOpBuffer(builder) : encodeOpBuffer(builder, info.content());
+
+ auto quantparam = encodeQuantizationParameters(builder, info.quantparam());
+
+ auto buffer_id = static_cast<uint32_t>(md._buffers.size());
+ md._buffers.push_back(buffer);
+
+ auto name_offset = builder.CreateString(info.name());
+ auto tensor_offset = CreateTensor(builder, shape_offset, info.dtype(), buffer_id, name_offset,
+ quantparam, /*is_variable*/ false);
+ gd._tensors.push_back(tensor_offset);
+}
+
+} // namespace
+
+namespace luci
+{
+
+void prepareModelData(FlatBufferBuilder &builder, SerializedModelData &md)
+{
+ // add one empty buffer
+ // note: this follows TFLite
+ // note: there's a comment in tflite fbs file
+ // - Note the 0th entry of this array must be an empty buffer (sentinel).
+ // - This is a convention so that tensors without a buffer can provide 0 as
+ // - their buffer.
+ auto buffer = encodeOpBuffer(builder);
+ md._buffers.push_back(buffer);
+}
+
+void exportOpDefinedTensors(loco::Graph *g, FlatBufferBuilder &builder, SerializedModelData &md,
+ SerializedGraphData &gd)
+{
+ CircleTensorContext tensor_ctx;
+
+ for (auto node : loco::postorder_traversal(loco::output_nodes(g)))
+ {
+ CircleNode *circle_node = dynamic_cast<luci::CircleNode *>(node);
+ allocateCircleTensor(circle_node, tensor_ctx);
+ }
+
+ for (const auto &tensor_info : tensor_ctx)
+ {
+ exportOpDefinedTensor(tensor_info, builder, md, gd);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/CircleTensorExporter.h b/compiler/luci/export/src/CircleTensorExporter.h
new file mode 100644
index 000000000..f9d6107b4
--- /dev/null
+++ b/compiler/luci/export/src/CircleTensorExporter.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __CIRCLE_TENSOR_EXPORTER_H__
+#define __CIRCLE_TENSOR_EXPORTER_H__
+
+#include "CircleExporterUtils.h"
+
+#include <loco/IR/Graph.h>
+
+#include <flatbuffers/flatbuffers.h>
+
+namespace luci
+{
+
+/**
+ * @brief one time preparation for SerializedModelData
+ */
+void prepareModelData(flatbuffers::FlatBufferBuilder &builder, SerializedModelData &md);
+
+/**
+ * @brief create Tensors corresponding to results of all nodes in graph
+ * @param computational graph
+ * @param gd information about serialized parts of model
+ */
+void exportOpDefinedTensors(loco::Graph *g, flatbuffers::FlatBufferBuilder &builder,
+ SerializedModelData &md, SerializedGraphData &gd);
+
+} // namespace luci
+
+#endif // __CIRCLE_TENSOR_EXPORTER_H__
diff --git a/compiler/luci/export/src/Optimize.cpp b/compiler/luci/export/src/Optimize.cpp
new file mode 100644
index 000000000..6fa50b564
--- /dev/null
+++ b/compiler/luci/export/src/Optimize.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "Optimize.h"
+#include "ProgressReporter.h"
+
+#include <luci/Pass/ShapeInferencePass.h>
+#include <luci/Pass/TypeInferencePass.h>
+
+#include <logo/Phase.h>
+
+#include <memory>
+
+namespace luci
+{
+
+void optimize(loco::Graph *g)
+{
+ logo::Phase phase;
+ {
+ // prepare type and shape before optimization
+ phase.emplace_back(std::make_unique<TypeInferencePass>());
+ phase.emplace_back(std::make_unique<ShapeInferencePass>());
+
+ // TODO add more optimization passes (with a knob)
+ }
+
+ logo::PhaseRunner<logo::PhaseStrategy::Restart> phase_runner{g};
+
+ ProgressReporter prog(g, logo::PhaseStrategy::Restart);
+ phase_runner.attach(&prog);
+ phase_runner.run(phase);
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/Optimize.h b/compiler/luci/export/src/Optimize.h
new file mode 100644
index 000000000..c3af7a04c
--- /dev/null
+++ b/compiler/luci/export/src/Optimize.h
@@ -0,0 +1,33 @@
+/*
+ * 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 __OPTIMIZE_H__
+#define __OPTIMIZE_H__
+
+#include <loco.h>
+
+namespace luci
+{
+
+/**
+ * @brief Run passes of graph transformations
+ *
+ */
+void optimize(loco::Graph *);
+
+} // namespace luci
+
+#endif // __OPTIMIZE_H__
diff --git a/compiler/luci/export/src/ProgressReporter.cpp b/compiler/luci/export/src/ProgressReporter.cpp
new file mode 100644
index 000000000..ac9c3d9a8
--- /dev/null
+++ b/compiler/luci/export/src/ProgressReporter.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "ProgressReporter.h"
+
+#include "luci/Log.h"
+#include "luci/LogHelper.h"
+
+#include <logo/Phase.h>
+#include <logo/Pass.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 luci
+{
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "==============================================================";
+ INFO(prime) << "luci::PhaseRunner<" << to_str(strategy()) << ">";
+ INFO(prime) << "Initial graph";
+ INFO(prime) << fmt(graph());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "luci::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) << fmt(graph());
+}
+
+} // namespace luci
diff --git a/compiler/luci/export/src/ProgressReporter.h b/compiler/luci/export/src/ProgressReporter.h
new file mode 100644
index 000000000..e91f42592
--- /dev/null
+++ b/compiler/luci/export/src/ProgressReporter.h
@@ -0,0 +1,53 @@
+/*
+ * 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 __PROGRESSREPORTER_H__
+#define __PROGRESSREPORTER_H__
+
+#include <logo/Phase.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+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 luci
+
+#endif // __PROGRESSREPORTER_H__
diff --git a/compiler/luci/export/src/SerializedData.h b/compiler/luci/export/src/SerializedData.h
new file mode 100644
index 000000000..84249653c
--- /dev/null
+++ b/compiler/luci/export/src/SerializedData.h
@@ -0,0 +1,95 @@
+/*
+ * 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 __SERIALIZED_DATA_H__
+#define __SERIALIZED_DATA_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <vector>
+
+#include <unordered_map>
+
+namespace luci
+{
+
+struct OpCode
+{
+ circle::BuiltinOperator opcode;
+
+ bool operator==(const OpCode &rhs) const { return opcode == rhs.opcode; }
+};
+
+} // namespace luci
+
+namespace std
+{
+
+template <> struct hash<luci::OpCode>
+{
+ size_t operator()(const luci::OpCode &x) const { return hash<int>()(x.opcode); }
+};
+
+} // namespace std
+
+namespace luci
+{
+
+/**
+ * @breif Record the information of T/F Lite SubGraph and its mapping to loco
+ */
+struct SubGraphContext
+{
+ /// @brief SubGraph input tensor id
+ std::vector<int32_t> _inputs;
+ /// @brief SubGraph output tensor id
+ std::vector<int32_t> _outputs;
+ /// @DataFormat for SubGraph
+ circle::DataFormat _data_format{circle::DataFormat::DataFormat_CHANNELS_LAST};
+};
+
+// Prerequisites for circle::Model object creation
+struct SerializedModelData final
+{
+ SerializedModelData() = default;
+ SerializedModelData(const SerializedModelData &) = delete;
+
+ std::unordered_map<OpCode, uint32_t> _operator_codes;
+ std::unordered_map<OpCode, std::string> _custom_operator_codes;
+ std::vector<flatbuffers::Offset<circle::Buffer>> _buffers;
+
+ /**
+ * @brief if opcode is not registered in table of opcodes add it
+ * @param builtin_code
+ * @return idx of opcode in table of opcodes (see schema)
+ */
+ uint32_t registerBuiltinOpcode(circle::BuiltinOperator builtin_code);
+ uint32_t registerCustomOpcode(const std::string &custom_op);
+};
+
+// Prerequisites for circle::Model object creation
+struct SerializedGraphData final : public SubGraphContext
+{
+ SerializedGraphData() = default;
+ SerializedGraphData(const SerializedModelData &) = delete;
+
+ std::vector<flatbuffers::Offset<circle::Operator>> _operators;
+ std::vector<flatbuffers::Offset<circle::Tensor>> _tensors;
+};
+
+} // namespace luci
+
+#endif // __SERIALIZED_DATA_H__
diff --git a/compiler/luci/import/CMakeLists.txt b/compiler/luci/import/CMakeLists.txt
new file mode 100644
index 000000000..bc9a9152a
--- /dev/null
+++ b/compiler/luci/import/CMakeLists.txt
@@ -0,0 +1,26 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_import SHARED ${SOURCES})
+target_include_directories(luci_import PRIVATE src)
+target_include_directories(luci_import PUBLIC include)
+target_link_libraries(luci_import PUBLIC luci_lang)
+target_link_libraries(luci_import PUBLIC mio_circle)
+target_link_libraries(luci_import PRIVATE luci_log)
+target_link_libraries(luci_import PRIVATE luci_logex)
+target_link_libraries(luci_import PRIVATE nncc_common)
+target_link_libraries(luci_import PRIVATE locop)
+target_link_libraries(luci_import PRIVATE oops)
+install(TARGETS luci_import DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_import_test ${TESTS})
+target_include_directories(luci_import_test PRIVATE src)
+target_link_libraries(luci_import_test luci_import)
+target_link_libraries(luci_import_test oops)
diff --git a/compiler/luci/import/README.md b/compiler/luci/import/README.md
new file mode 100644
index 000000000..4ae81ff67
--- /dev/null
+++ b/compiler/luci/import/README.md
@@ -0,0 +1,3 @@
+# luci-import
+
+_luci-import_ provides importing Circle model file to _loco_ graph of _luci_ Circle Dialect IR
diff --git a/compiler/luci/import/include/luci/Import/CircleReader.h b/compiler/luci/import/include/luci/Import/CircleReader.h
new file mode 100644
index 000000000..fcbe09ceb
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/CircleReader.h
@@ -0,0 +1,87 @@
+/*
+ * 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 __LUCI_IMPORT_GRAPHREADER_H__
+#define __LUCI_IMPORT_GRAPHREADER_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <luci/IR/AttrFusedActFunc.h>
+#include <luci/IR/AttrPadding.h>
+#include <luci/IR/CircleQuantParam.h>
+
+#include <loco.h>
+
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace luci
+{
+
+bool is_valid(const circle::OperatorCodeT &opcode);
+bool is_custom(const circle::OperatorCodeT &opcode);
+std::string opcode_name(const circle::OperatorCodeT &opcode);
+const char *tensor_name(const circle::TensorT &tensor);
+const circle::QuantizationParametersT *tensor_quantization(const circle::TensorT &tensor);
+
+loco::DataType luci_datatype(circle::TensorType type);
+FusedActFunc luci_actfunc(const circle::ActivationFunctionType type);
+Padding luci_padding(const circle::Padding padding);
+std::unique_ptr<CircleQuantParam>
+luci_quantparam(const circle::QuantizationParametersT *quantization);
+
+/**
+ * @brief Loads Circle file and provides helpers to access attributes
+ */
+class CircleReader
+{
+private:
+ using CircleBuffers_t = std::vector<std::unique_ptr<circle::BufferT>>;
+ using CircleTensors_t = std::vector<std::unique_ptr<circle::TensorT>>;
+ using CircleOperators_t = std::vector<std::unique_ptr<circle::OperatorT>>;
+ using CircleOperatorCodes_t = std::vector<std::unique_ptr<circle::OperatorCodeT>>;
+
+public:
+ CircleReader() = default;
+
+public:
+ const CircleOperatorCodes_t &opcodes() const { return _model->operator_codes; }
+ const CircleBuffers_t &buffers() const { return _model->buffers; }
+ const CircleTensors_t &tensors() const { return _current_subgraph->tensors; }
+ const CircleOperators_t &operators() const { return _current_subgraph->operators; }
+ const std::vector<int32_t> &inputs() const { return _current_subgraph->inputs; }
+ const std::vector<int32_t> &outputs() const { return _current_subgraph->outputs; }
+ const std::string &name() const { return _current_subgraph->name; }
+
+ uint32_t num_subgraph() const { return _model->subgraphs.size(); }
+
+ circle::BuiltinOperator builtin_code(const circle::OperatorT &op) const;
+ std::string opcode_name(const circle::OperatorT &op) const;
+
+public:
+ bool parse(const circle::Model *model);
+ bool select_subgraph(uint32_t subgraph);
+
+private:
+ std::unique_ptr<const circle::ModelT> _model;
+ const circle::SubGraphT *_current_subgraph{nullptr};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPHREADER_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilder.h b/compiler/luci/import/include/luci/Import/GraphBuilder.h
new file mode 100644
index 000000000..61f673fb6
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilder.h
@@ -0,0 +1,56 @@
+/*
+ * 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 __LUCI_IMPORT_GRAPH_BUILDER_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_H__
+
+#include "GraphBuilderContext.h"
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+/**
+ * @brief Interface of convert circle:: NodeDef to loco::Node (e.g., Conv2DGraphBuilder)
+ */
+class GraphBuilder
+{
+public:
+ struct ValidateArgs
+ {
+ ValidateArgs(const circle::OperatorT &o, const CircleReader &r) : op(o), reader(r) {}
+
+ const circle::OperatorT &op;
+ const CircleReader &reader;
+ };
+
+public:
+ virtual ~GraphBuilder() = default;
+
+ virtual bool validate(const ValidateArgs &) const = 0;
+
+ void build(const circle::OperatorT &op, GraphBuilderContext *context) const;
+
+private:
+ virtual CircleNode *build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderContext.h b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h
new file mode 100644
index 000000000..8d464181d
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilderContext.h
@@ -0,0 +1,79 @@
+/*
+ * 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 __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__
+
+#include "CircleReader.h"
+
+#include <luci/IR/CircleNode.h>
+
+#include <loco.h>
+
+#include <map>
+
+namespace luci
+{
+
+using TensorIndex = int32_t;
+
+/*
+ * @brief Tensor Index to CircleNode
+ * To find CircleNode from TensorIndex
+ */
+class IndexNodeFinder
+{
+public:
+ void enroll(TensorIndex idx, CircleNode *node);
+
+ CircleNode *node(TensorIndex idx) const;
+
+private:
+ using MapIndexNode_t = std::map<TensorIndex, CircleNode *>;
+
+ MapIndexNode_t _table;
+};
+
+/**
+ * @brief Class to store context to build loco graph IR from TensorFlow
+ */
+class GraphBuilderContext
+{
+public:
+ GraphBuilderContext(loco::Graph *g, CircleReader *reader, IndexNodeFinder *nodefinder)
+ : _g(g), _reader(reader), _indexnodefinder(nodefinder)
+ {
+ // DO NOTHING
+ }
+
+ GraphBuilderContext(const GraphBuilderContext &) = delete;
+ GraphBuilderContext(GraphBuilderContext &&) = delete;
+
+public:
+ loco::Graph *graph() { return _g; }
+ CircleReader *reader() { return _reader; }
+
+ IndexNodeFinder *nodefinder() { return _indexnodefinder; }
+
+private:
+ loco::Graph *_g;
+ CircleReader *_reader;
+ IndexNodeFinder *_indexnodefinder;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_CONTEXT_H__
diff --git a/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h b/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h
new file mode 100644
index 000000000..99054e7b6
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/GraphBuilderRegistry.h
@@ -0,0 +1,85 @@
+/*
+ * 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 __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__
+#define __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__
+
+#include "GraphBuilder.h"
+
+#include <map>
+
+namespace luci
+{
+
+struct GraphBuilderSource
+{
+ virtual ~GraphBuilderSource() = default;
+
+ /**
+ * @brief Returns registered GraphBuilder pointer for operator (nullptr if not present)
+ */
+ virtual const GraphBuilder *lookup(const circle::BuiltinOperator &op) const = 0;
+};
+
+/**
+ * @brief Class to return graph builder for TF nodes
+ */
+class GraphBuilderRegistry final : public GraphBuilderSource
+{
+public:
+ GraphBuilderRegistry();
+
+public:
+ GraphBuilderRegistry(const GraphBuilderSource *parent) : _parent{parent}
+ {
+ // DO NOTHING
+ }
+
+public:
+ /**
+ * @brief Returns registered GraphBuilder pointer for operator or
+ * nullptr if not registered
+ */
+ const GraphBuilder *lookup(const circle::BuiltinOperator &op) const final
+ {
+ if (_builder_map.find(op) == _builder_map.end())
+ return (_parent == nullptr) ? nullptr : _parent->lookup(op);
+
+ return _builder_map.at(op).get();
+ }
+
+ static GraphBuilderRegistry &get()
+ {
+ static GraphBuilderRegistry me;
+ return me;
+ }
+
+public:
+ void add(const circle::BuiltinOperator op, std::unique_ptr<GraphBuilder> &&builder)
+ {
+ _builder_map[op] = std::move(builder);
+ }
+
+private:
+ const GraphBuilderSource *_parent = nullptr;
+
+private:
+ std::map<const circle::BuiltinOperator, std::unique_ptr<GraphBuilder>> _builder_map;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_GRAPH_BUILDER_REGISTRY_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes.h b/compiler/luci/import/include/luci/Import/Nodes.h
new file mode 100644
index 000000000..381d02b97
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __LUCI_IMPORT_NODES_H__
+#define __LUCI_IMPORT_NODES_H__
+
+#include "Nodes/CircleAbs.h"
+#include "Nodes/CircleAdd.h"
+#include "Nodes/CircleArgMax.h"
+#include "Nodes/CircleAveragePool2D.h"
+#include "Nodes/CircleBatchToSpaceND.h"
+#include "Nodes/CircleConcatenation.h"
+#include "Nodes/CircleConst.h"
+#include "Nodes/CircleConv2D.h"
+#include "Nodes/CircleCos.h"
+#include "Nodes/CircleDepthwiseConv2D.h"
+#include "Nodes/CircleDiv.h"
+#include "Nodes/CircleEqual.h"
+#include "Nodes/CircleExp.h"
+#include "Nodes/CircleFullyConnected.h"
+#include "Nodes/CircleLogicalNot.h"
+#include "Nodes/CircleLogicalOr.h"
+#include "Nodes/CircleMaxPool2D.h"
+#include "Nodes/CircleMean.h"
+#include "Nodes/CircleMul.h"
+#include "Nodes/CirclePack.h"
+#include "Nodes/CirclePad.h"
+#include "Nodes/CircleRelu.h"
+#include "Nodes/CircleReshape.h"
+#include "Nodes/CircleRsqrt.h"
+#include "Nodes/CircleSoftmax.h"
+#include "Nodes/CircleSub.h"
+#include "Nodes/CircleTranspose.h"
+
+#endif // __LUCI_IMPORT_NODES_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h
new file mode 100644
index 000000000..e0cec26d9
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAbs.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_ABS_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ABS_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAbsGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ABS_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h
new file mode 100644
index 000000000..d852ee8b3
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAdd.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_ADD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ADD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAddGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ADD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h
new file mode 100644
index 000000000..dae4691dc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleArgMax.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__
+#define __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleArgMaxGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_ARGMAX_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h
new file mode 100644
index 000000000..07f6565bc
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleAveragePool2D.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleAveragePool2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_AVERAGEPOOL2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h
new file mode 100644
index 000000000..4168d248e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleBatchToSpaceND.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__
+#define __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleBatchToSpaceNDGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_BATCHTOSPACEND_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h
new file mode 100644
index 000000000..9b4c9ffd1
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConcatenation.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleConcatenationGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CONCATENATION_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h
new file mode 100644
index 000000000..7d4f10a59
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConst.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_CONST_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CONST_H__
+
+#include "luci/Import/GraphBuilderContext.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+
+/*
+ * @note Circle does not have Const operator.
+ * Methods here provide helper that creates CircleConst from
+ * Tensor and Buffer in circle flatbuffer file.
+ */
+
+namespace luci
+{
+
+CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index);
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CONST_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h
new file mode 100644
index 000000000..4529a4f11
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleConv2D.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleConv2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_CONV_2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h b/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h
new file mode 100644
index 000000000..fb472977e
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleCos.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_COS_H__
+#define __LUCI_IMPORT_OP_CIRCLE_COS_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleCosGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_COS_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h
new file mode 100644
index 000000000..1953cb76c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDepthwiseConv2D.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDepthwiseConv2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DEPTHWISECONV_2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h b/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h
new file mode 100644
index 000000000..6a38118fe
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleDiv.h
@@ -0,0 +1,36 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_DIV_H__
+#define __LUCI_IMPORT_OP_CIRCLE_DIV_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleDivGraphBuilder : public GraphBuilder
+{
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_DIV_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h b/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h
new file mode 100644
index 000000000..a98adcd08
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleEqual.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleEqualGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_EQUAL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h b/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h
new file mode 100644
index 000000000..521809fe4
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleExp.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_EXP_H__
+#define __LUCI_IMPORT_OP_CIRCLE_EXP_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleExpGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_EXP_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h b/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h
new file mode 100644
index 000000000..b7798c688
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleFullyConnected.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__
+#define __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleFullyConnectedGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_FULLYCONNECTED_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h
new file mode 100644
index 000000000..ec890ecf7
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalNot.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogicalNotGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALNOT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h
new file mode 100644
index 000000000..9fb0086c1
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleLogicalOr.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__
+#define __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleLogicalOrGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_LOGICALOR_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h
new file mode 100644
index 000000000..bcd2acb30
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMaxPool2D.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMaxPool2DGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MAXPOOL2D_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h
new file mode 100644
index 000000000..a7919a57c
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMean.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_MEAN_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MEAN_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMeanGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MEAN_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h b/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h
new file mode 100644
index 000000000..13027a155
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleMul.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_MUL_H__
+#define __LUCI_IMPORT_OP_CIRCLE_MUL_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleMulGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_MUL_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h
new file mode 100644
index 000000000..8e4b71995
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePack.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_PACK_H__
+#define __LUCI_IMPORT_OP_CIRCLE_PACK_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePackGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_PACK_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h b/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h
new file mode 100644
index 000000000..e333ee912
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CirclePad.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_PAD_H__
+#define __LUCI_IMPORT_OP_CIRCLE_PAD_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CirclePadGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_PAD_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h
new file mode 100644
index 000000000..deb913243
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRelu.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_RELU_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RELU_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReluGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RELU_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h b/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h
new file mode 100644
index 000000000..eb4fb13ba
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleReshape.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleReshapeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RESHAPE_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h b/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h
new file mode 100644
index 000000000..90d568f1f
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleRsqrt.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__
+#define __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleRsqrtGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_RSQRT_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h
new file mode 100644
index 000000000..b93846d67
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSoftmax.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSoftmaxGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SOFTMAX_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h b/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h
new file mode 100644
index 000000000..315d1c2f9
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleSub.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_SUB_H__
+#define __LUCI_IMPORT_OP_CIRCLE_SUB_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleSubGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_SUB_H__
diff --git a/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h b/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h
new file mode 100644
index 000000000..ac0f1fb41
--- /dev/null
+++ b/compiler/luci/import/include/luci/Import/Nodes/CircleTranspose.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__
+#define __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__
+
+#include "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+class CircleTransposeGraphBuilder : public GraphBuilder
+{
+public:
+ bool validate(const ValidateArgs &args) const final;
+
+private:
+ CircleNode *build_node(const circle::OperatorT &op, const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const override;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IMPORT_OP_CIRCLE_TRANSPOSE_H__
diff --git a/compiler/luci/import/include/luci/Importer.h b/compiler/luci/import/include/luci/Importer.h
new file mode 100644
index 000000000..246df9f27
--- /dev/null
+++ b/compiler/luci/import/include/luci/Importer.h
@@ -0,0 +1,54 @@
+/*
+ * 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 __LUCI_IMPORTER_H__
+#define __LUCI_IMPORTER_H__
+
+#include "luci/Import/GraphBuilderRegistry.h"
+
+#include "luci/IR/Module.h"
+
+#include <loco.h>
+
+#include <mio/circle/schema_generated.h>
+
+#include <memory>
+
+namespace luci
+{
+
+class Importer final
+{
+public:
+ Importer();
+
+public:
+ explicit Importer(const GraphBuilderSource *source) : _source{source}
+ {
+ // DO NOTHING
+ }
+
+public:
+ std::unique_ptr<loco::Graph> import(const circle::Model *model) const;
+ std::unique_ptr<Module> importModule(const circle::Model *model) const;
+
+private:
+ const GraphBuilderSource *_source = nullptr;
+};
+
+} // namespace luci
+
+#endif // __MOCO_IMPORTER_H__
diff --git a/compiler/luci/import/src/CircleReader.cpp b/compiler/luci/import/src/CircleReader.cpp
new file mode 100644
index 000000000..ead0093b8
--- /dev/null
+++ b/compiler/luci/import/src/CircleReader.cpp
@@ -0,0 +1,211 @@
+/*
+ * 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 "luci/Import/CircleReader.h"
+
+#include <memory>
+#include <sstream>
+#include <string>
+
+namespace luci
+{
+
+bool is_valid(const circle::OperatorCodeT &opcode)
+{
+ circle::BuiltinOperator code = opcode.builtin_code;
+ return (circle::BuiltinOperator_MIN <= code && code <= circle::BuiltinOperator_MAX);
+}
+
+bool is_custom(const circle::OperatorCodeT &opcode)
+{
+ circle::BuiltinOperator code = opcode.builtin_code;
+ return (code == circle::BuiltinOperator_CUSTOM);
+}
+
+std::string opcode_name(const circle::OperatorCodeT &opcode)
+{
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid)";
+ return oss.str();
+ }
+
+ if (is_custom(opcode))
+ {
+ if (opcode.custom_code.empty())
+ return "(invalid custom)";
+
+ return opcode.custom_code;
+ }
+
+ circle::BuiltinOperator code = opcode.builtin_code;
+ return circle::EnumNameBuiltinOperator(code);
+}
+
+const char *tensor_name(const circle::TensorT &tensor)
+{
+ static const char *kEmptyTensorName = "(noname)";
+
+ if (!tensor.name.empty())
+ return tensor.name.c_str();
+
+ return kEmptyTensorName;
+}
+
+const circle::QuantizationParametersT *tensor_quantization(const circle::TensorT &tensor)
+{
+ return tensor.quantization.get();
+}
+
+loco::DataType luci_datatype(const circle::TensorType type)
+{
+ switch (type)
+ {
+ case circle::TensorType_FLOAT32:
+ return loco::DataType::FLOAT32;
+ case circle::TensorType_FLOAT16:
+ return loco::DataType::FLOAT16;
+ case circle::TensorType_INT32:
+ return loco::DataType::S32;
+ case circle::TensorType_UINT8:
+ return loco::DataType::U8;
+ case circle::TensorType_INT64:
+ return loco::DataType::S64;
+ case circle::TensorType_STRING:
+ break;
+ case circle::TensorType_BOOL:
+ return loco::DataType::BOOL;
+ case circle::TensorType_INT16:
+ return loco::DataType::S16;
+ case circle::TensorType_COMPLEX64:
+ break;
+ case circle::TensorType_INT8:
+ return loco::DataType::S8;
+ default:
+ break;
+ }
+ assert(false);
+ return loco::DataType::Unknown;
+}
+
+FusedActFunc luci_actfunc(const circle::ActivationFunctionType type)
+{
+ switch (type)
+ {
+ case circle::ActivationFunctionType::ActivationFunctionType_NONE:
+ return luci::FusedActFunc::NONE;
+ case circle::ActivationFunctionType::ActivationFunctionType_RELU:
+ return luci::FusedActFunc::RELU;
+ case circle::ActivationFunctionType::ActivationFunctionType_RELU_N1_TO_1:
+ return luci::FusedActFunc::RELU_N1_TO_1;
+ case circle::ActivationFunctionType::ActivationFunctionType_RELU6:
+ return luci::FusedActFunc::RELU6;
+ case circle::ActivationFunctionType::ActivationFunctionType_TANH:
+ break;
+ default:
+ break;
+ }
+ assert(false);
+ return luci::FusedActFunc::UNDEFINED;
+}
+
+Padding luci_padding(const circle::Padding padding)
+{
+ switch (padding)
+ {
+ case circle::Padding::Padding_SAME:
+ return Padding::SAME;
+ case circle::Padding::Padding_VALID:
+ return Padding::VALID;
+ }
+ assert(false);
+ return Padding::UNDEFINED;
+}
+
+std::unique_ptr<CircleQuantParam>
+luci_quantparam(const circle::QuantizationParametersT *quantization)
+{
+ const auto &min = quantization->min;
+ const auto &max = quantization->max;
+ const auto &scale = quantization->scale;
+ const auto &zero_point = quantization->zero_point;
+
+ if ((!min.empty() && !max.empty()) || (!scale.empty() && !zero_point.empty()))
+ {
+ auto quantparam = std::make_unique<CircleQuantParam>();
+
+ quantparam->min = min;
+ quantparam->max = max;
+ quantparam->scale = scale;
+ quantparam->zerop = zero_point;
+
+ return quantparam;
+ }
+
+ return nullptr;
+}
+
+circle::BuiltinOperator CircleReader::builtin_code(const circle::OperatorT &op) const
+{
+ const auto &op_codes = opcodes();
+ uint32_t index = op.opcode_index;
+ assert(index < op_codes.size());
+ const circle::OperatorCodeT &opcode = *op_codes[index];
+
+ return opcode.builtin_code;
+}
+
+std::string CircleReader::opcode_name(const circle::OperatorT &op) const
+{
+ const auto &op_codes = opcodes();
+ uint32_t index = op.opcode_index;
+ assert(index < op_codes.size());
+ const circle::OperatorCodeT &opcode = *op_codes[index];
+
+ if (!is_valid(opcode))
+ {
+ std::ostringstream oss;
+ oss << "(invalid: " << index << ")";
+ return oss.str();
+ }
+
+ return ::luci::opcode_name(opcode);
+}
+
+bool CircleReader::parse(const circle::Model *model)
+{
+ assert(model != nullptr);
+
+ _model.reset(model->UnPack());
+
+ return true;
+}
+
+bool CircleReader::select_subgraph(uint32_t sgindex)
+{
+ if (_model->subgraphs.size() <= sgindex)
+ {
+ assert(false);
+ return false;
+ }
+
+ _current_subgraph = _model->subgraphs[sgindex].get();
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/GraphBuilder.cpp b/compiler/luci/import/src/GraphBuilder.cpp
new file mode 100644
index 000000000..e0ec9ded5
--- /dev/null
+++ b/compiler/luci/import/src/GraphBuilder.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "luci/Import/GraphBuilder.h"
+
+namespace luci
+{
+
+void GraphBuilder::build(const circle::OperatorT &op, GraphBuilderContext *context) const
+{
+ assert(context != nullptr);
+
+ const std::vector<int32_t> &inputs = op.inputs;
+ const std::vector<int32_t> &outputs = op.outputs;
+ const auto &tensors = context->reader()->tensors();
+
+ std::vector<CircleNode *> input_nodes;
+ for (const int32_t input_tensor_index : inputs)
+ {
+ input_nodes.push_back(context->nodefinder()->node(input_tensor_index));
+ }
+
+ CircleNode *node = build_node(op, input_nodes, context->graph());
+
+ // Set up node parameters.
+ assert(outputs.size() == 1);
+ {
+ const circle::TensorT &output_tensor = *tensors[outputs[0]];
+
+ node->name(tensor_name(output_tensor));
+
+ auto quantization = tensor_quantization(output_tensor);
+ if (quantization)
+ {
+ auto quantparam = luci_quantparam(quantization);
+ if (quantparam)
+ node->quantparam(std::move(quantparam));
+ }
+ }
+
+ // Register node's only output.
+ assert(outputs.size() == 1);
+ {
+ context->nodefinder()->enroll(outputs[0], node);
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/GraphBuilderContext.cpp b/compiler/luci/import/src/GraphBuilderContext.cpp
new file mode 100644
index 000000000..a5162ce83
--- /dev/null
+++ b/compiler/luci/import/src/GraphBuilderContext.cpp
@@ -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.
+ */
+
+#include "luci/Import/GraphBuilderContext.h"
+
+#include <luci/Log.h>
+
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+void IndexNodeFinder::enroll(TensorIndex idx, CircleNode *node)
+{
+ if (_table.find(idx) != _table.end())
+ {
+ LOGGER(l);
+ INFO(l) << "[luci] NodeFinder SKIP (" << idx << ") " << node << std::endl;
+ return;
+ }
+
+ _table[idx] = node;
+}
+
+CircleNode *IndexNodeFinder::node(TensorIndex idx) const
+{
+ MapIndexNode_t::const_iterator iter = _table.find(idx);
+
+ assert(iter != _table.end() && iter->second != nullptr);
+
+ return iter->second;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/GraphBuilderRegistry.cpp b/compiler/luci/import/src/GraphBuilderRegistry.cpp
new file mode 100644
index 000000000..929b71a7d
--- /dev/null
+++ b/compiler/luci/import/src/GraphBuilderRegistry.cpp
@@ -0,0 +1,163 @@
+/*
+ * 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 "luci/Import/GraphBuilderRegistry.h"
+
+#include "luci/Import/Nodes.h"
+
+#include <memory>
+
+namespace luci
+{
+
+GraphBuilderRegistry::GraphBuilderRegistry()
+{
+#define CIRCLE_NODE(OPCODE, CLASS) add(circle::BuiltinOperator_##OPCODE, std::make_unique<CLASS>());
+
+ CIRCLE_NODE(ABS, CircleAbsGraphBuilder); // 101
+ CIRCLE_NODE(ADD, CircleAddGraphBuilder); // 0
+ CIRCLE_NODE(ARG_MAX, CircleArgMaxGraphBuilder); // 56
+ CIRCLE_NODE(AVERAGE_POOL_2D, CircleAveragePool2DGraphBuilder); // 1
+ CIRCLE_NODE(BATCH_TO_SPACE_ND, CircleBatchToSpaceNDGraphBuilder); // 37
+ CIRCLE_NODE(CONCATENATION, CircleConcatenationGraphBuilder); // 2
+ CIRCLE_NODE(CONV_2D, CircleConv2DGraphBuilder); // 3
+ CIRCLE_NODE(COS, CircleCosGraphBuilder); // 108
+ CIRCLE_NODE(DEPTHWISE_CONV_2D, CircleDepthwiseConv2DGraphBuilder); // 4
+ CIRCLE_NODE(DIV, CircleDivGraphBuilder); // 42
+ CIRCLE_NODE(EQUAL, CircleEqualGraphBuilder); // 71
+ CIRCLE_NODE(EXP, CircleExpGraphBuilder); // 47
+ CIRCLE_NODE(FULLY_CONNECTED, CircleFullyConnectedGraphBuilder); // 9
+ CIRCLE_NODE(LOGICAL_NOT, CircleLogicalNotGraphBuilder); // 87
+ CIRCLE_NODE(LOGICAL_OR, CircleLogicalOrGraphBuilder); // 84
+ CIRCLE_NODE(MAX_POOL_2D, CircleMaxPool2DGraphBuilder); // 17
+ CIRCLE_NODE(MEAN, CircleMeanGraphBuilder); // 40
+ CIRCLE_NODE(MUL, CircleMulGraphBuilder); // 18
+ CIRCLE_NODE(PACK, CirclePackGraphBuilder); // 83
+ CIRCLE_NODE(PAD, CirclePadGraphBuilder); // 34
+ CIRCLE_NODE(RELU, CircleReluGraphBuilder); // 19
+ CIRCLE_NODE(RESHAPE, CircleReshapeGraphBuilder); // 22
+ CIRCLE_NODE(RSQRT, CircleRsqrtGraphBuilder); // 76
+ CIRCLE_NODE(SOFTMAX, CircleSoftmaxGraphBuilder); // 25
+ CIRCLE_NODE(SUB, CircleSubGraphBuilder); // 41
+ CIRCLE_NODE(TRANSPOSE, CircleTransposeGraphBuilder); // 39
+
+#undef CIRCLE_NODE
+
+ // BuiltinOperator_DEQUANTIZE = 6,
+ // BuiltinOperator_EMBEDDING_LOOKUP = 7,
+ // BuiltinOperator_FLOOR = 8,
+ // BuiltinOperator_HASHTABLE_LOOKUP = 10,
+ // BuiltinOperator_L2_NORMALIZATION = 11,
+ // BuiltinOperator_L2_POOL_2D = 12,
+ // BuiltinOperator_LOCAL_RESPONSE_NORMALIZATION = 13,
+ // BuiltinOperator_LOGISTIC = 14,
+ // BuiltinOperator_LSH_PROJECTION = 15,
+ // BuiltinOperator_LSTM = 16,
+ // BuiltinOperator_RELU_N1_TO_1 = 20,
+ // BuiltinOperator_RELU6 = 21,
+ // BuiltinOperator_RESIZE_BILINEAR = 23,
+ // BuiltinOperator_RNN = 24,
+ // BuiltinOperator_SPACE_TO_DEPTH = 26,
+ // BuiltinOperator_SVDF = 27,
+ // BuiltinOperator_TANH = 28,
+ // BuiltinOperator_CONCAT_EMBEDDINGS = 29,
+ // BuiltinOperator_SKIP_GRAM = 30,
+ // BuiltinOperator_CALL = 31,
+ // BuiltinOperator_CUSTOM = 32,
+ // BuiltinOperator_EMBEDDING_LOOKUP_SPARSE = 33,
+ // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_RNN = 35,
+ // BuiltinOperator_GATHER = 36,
+ // BuiltinOperator_SPACE_TO_BATCH_ND = 38,
+ // BuiltinOperator_SQUEEZE = 43,
+ // BuiltinOperator_UNIDIRECTIONAL_SEQUENCE_LSTM = 44,
+ // BuiltinOperator_STRIDED_SLICE = 45,
+ // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_RNN = 46,
+ // BuiltinOperator_TOPK_V2 = 48,
+ // BuiltinOperator_SPLIT = 49,
+ // BuiltinOperator_LOG_SOFTMAX = 50,
+ // BuiltinOperator_DELEGATE = 51,
+ // BuiltinOperator_BIDIRECTIONAL_SEQUENCE_LSTM = 52,
+ // BuiltinOperator_CAST = 53,
+ // BuiltinOperator_PRELU = 54,
+ // BuiltinOperator_MAXIMUM = 55,
+ // BuiltinOperator_ARG_MAX = 56,
+ // BuiltinOperator_MINIMUM = 57,
+ // BuiltinOperator_LESS = 58,
+ // BuiltinOperator_NEG = 59,
+ // BuiltinOperator_PADV2 = 60,
+ // BuiltinOperator_GREATER = 61,
+ // BuiltinOperator_GREATER_EQUAL = 62,
+ // BuiltinOperator_LESS_EQUAL = 63,
+ // BuiltinOperator_SELECT = 64,
+ // BuiltinOperator_SLICE = 65,
+ // BuiltinOperator_SIN = 66,
+ // BuiltinOperator_TRANSPOSE_CONV = 67,
+ // BuiltinOperator_SPARSE_TO_DENSE = 68,
+ // BuiltinOperator_TILE = 69,
+ // BuiltinOperator_EXPAND_DIMS = 70,
+ // BuiltinOperator_NOT_EQUAL = 72,
+ // BuiltinOperator_LOG = 73,
+ // BuiltinOperator_SUM = 74,
+ // BuiltinOperator_SQRT = 75,
+ // BuiltinOperator_SHAPE = 77,
+ // BuiltinOperator_POW = 78,
+ // BuiltinOperator_ARG_MIN = 79,
+ // BuiltinOperator_FAKE_QUANT = 80,
+ // BuiltinOperator_REDUCE_PROD = 81,
+ // BuiltinOperator_REDUCE_MAX = 82,
+ // BuiltinOperator_ONE_HOT = 85,
+ // BuiltinOperator_LOGICAL_AND = 86,
+ // BuiltinOperator_UNPACK = 88,
+ // BuiltinOperator_REDUCE_MIN = 89,
+ // BuiltinOperator_FLOOR_DIV = 90,
+ // BuiltinOperator_REDUCE_ANY = 91,
+ // BuiltinOperator_SQUARE = 92,
+ // BuiltinOperator_ZEROS_LIKE = 93,
+ // BuiltinOperator_FILL = 94,
+ // BuiltinOperator_FLOOR_MOD = 95,
+ // BuiltinOperator_RANGE = 96,
+ // BuiltinOperator_RESIZE_NEAREST_NEIGHBOR = 97,
+ // BuiltinOperator_LEAKY_RELU = 98,
+ // BuiltinOperator_SQUARED_DIFFERENCE = 99,
+ // BuiltinOperator_MIRROR_PAD = 100,
+ // BuiltinOperator_SPLIT_V = 102,
+ // BuiltinOperator_UNIQUE = 103,
+ // BuiltinOperator_CEIL = 104,
+ // BuiltinOperator_REVERSE_V2 = 105,
+ // BuiltinOperator_ADD_N = 106,
+ // BuiltinOperator_GATHER_ND = 107,
+ // BuiltinOperator_WHERE = 109,
+ // BuiltinOperator_RANK = 110,
+ // BuiltinOperator_ELU = 111,
+ // BuiltinOperator_REVERSE_SEQUENCE = 112,
+ // BuiltinOperator_MATRIX_DIAG = 113,
+ // BuiltinOperator_QUANTIZE = 114,
+ // BuiltinOperator_MATRIX_SET_DIAG = 115,
+ // BuiltinOperator_ROUND = 116,
+ // BuiltinOperator_HARD_SWISH = 117,
+ // BuiltinOperator_IF = 118,
+ // BuiltinOperator_WHILE = 119,
+ // BuiltinOperator_NON_MAX_SUPPRESSION_V4 = 120,
+ // BuiltinOperator_NON_MAX_SUPPRESSION_V5 = 121,
+ // BuiltinOperator_SCATTER_ND = 122,
+ // BuiltinOperator_SELECT_V2 = 123,
+ // BuiltinOperator_DENSIFY = 124,
+ // BuiltinOperator_SEGMENT_SUM = 125,
+ // BuiltinOperator_BATCH_MATMUL = 126,
+ // BuiltinOperator_INSTANCE_NORM = 254,
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Importer.cpp b/compiler/luci/import/src/Importer.cpp
new file mode 100644
index 000000000..964c47633
--- /dev/null
+++ b/compiler/luci/import/src/Importer.cpp
@@ -0,0 +1,253 @@
+/*
+ * 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 "luci/Importer.h"
+
+#include "luci/Import/GraphBuilder.h"
+#include "luci/Import/GraphBuilderContext.h"
+#include "luci/Import/GraphBuilderRegistry.h"
+#include "luci/Import/CircleReader.h"
+#include "luci/Import/Nodes/CircleConst.h"
+
+#include <luci/IR/Module.h>
+#include <luci/IR/CircleNodes.h>
+#include <luci/Log.h>
+#include <luci/LogHelper.h>
+
+#include <oops/UserExn.h>
+
+#include <memory>
+
+namespace
+{
+
+void convert_graph(const luci::GraphBuilderSource &source, luci::CircleReader &reader,
+ loco::Graph *graph)
+{
+ LOGGER(l);
+
+ auto nodefinder = std::make_unique<luci::IndexNodeFinder>();
+
+ luci::GraphBuilderContext gb_context(graph, &reader, nodefinder.get());
+
+ const auto &operators = reader.operators();
+ const auto &tensors = reader.tensors();
+
+ // graph inputs; there are no input nodes in TFlite but just Tensors
+ // creating virtual input nodes will make possible to connect nodes that uses them
+ // all attributes of tensor should be copied to CircleInput node
+ for (const auto input : reader.inputs())
+ {
+ auto input_node = graph->nodes()->create<luci::CircleInput>();
+ assert(input_node != nullptr);
+ const circle::TensorT &tensor = *tensors[input];
+
+ auto tname = luci::tensor_name(tensor);
+ input_node->name(tname);
+ auto quantization = luci::tensor_quantization(tensor);
+ if (quantization)
+ {
+ auto quantparam = luci::luci_quantparam(quantization);
+ if (quantparam.get())
+ input_node->quantparam(std::move(quantparam));
+ }
+
+ INFO(l) << "[luci] NodeFinder INPUT(" << input << ") = " << input_node << std::endl;
+ nodefinder->enroll(input, input_node);
+
+ // Shape of Input
+ const std::vector<int32_t> &input_dims = tensor.shape; // in NHWC
+ input_node->rank(input_dims.size());
+ for (uint32_t r = 0; r < input_dims.size(); ++r)
+ input_node->dim(r) = loco::Dimension(input_dims[r]);
+
+ // Data type of Input
+ auto dtype = luci::luci_datatype(tensor.type);
+ input_node->dtype(dtype);
+
+ // Name
+ auto graph_input = graph->inputs()->create();
+ graph_input->name(tname);
+
+ // Set GraphInputOutputIndex for graph
+ input_node->index(graph_input->index());
+
+ // Data type
+ graph_input->dtype(dtype);
+ }
+
+ // Create CircleConst nodes for constant tensors.
+ const auto &buffers = reader.buffers();
+ for (uint32_t i = 0; i < tensors.size(); ++i)
+ {
+ const circle::TensorT &tensor = *tensors[i];
+ const std::vector<uint8_t> &buffer = buffers[tensor.buffer]->data;
+ if (!buffer.empty())
+ {
+ luci::CircleConst *const_node = luci::create_circleconst(&gb_context, i);
+ nodefinder->enroll(i, const_node);
+ }
+ }
+
+ // Import the operators.
+ // Note that operators in model are stored in execution order. This means that when importing
+ // an operator, its input operators have already been imported. We exploit this fact to set up
+ // node's inputs right after creating the node.
+ for (uint32_t i = 0; i < operators.size(); ++i)
+ {
+ const circle::OperatorT &op = *operators[i];
+ circle::BuiltinOperator builtincode = reader.builtin_code(op);
+
+ if (const auto *builder = source.lookup(builtincode))
+ {
+ luci::GraphBuilder::ValidateArgs args(op, reader);
+ if (!builder->validate(args))
+ {
+ throw oops::UserExn("Invalid operator", reader.opcode_name(op));
+ }
+
+ builder->build(op, &gb_context);
+ }
+ else
+ {
+ throw oops::UserExn("Not supported", reader.opcode_name(op));
+ }
+ }
+
+ // graph outputs
+ for (auto output : reader.outputs())
+ {
+ auto output_node = graph->nodes()->create<luci::CircleOutput>();
+ assert(output_node != nullptr);
+ output_node->from(nodefinder->node(output));
+
+ INFO(l) << "[luci] NodeFinder OUTPUT(" << output << ") = " << output_node << std::endl;
+
+ // set the graph output name and node object
+ const circle::TensorT &tensor = *tensors[output];
+ auto graph_output = graph->outputs()->create();
+ std::string tname = luci::tensor_name(tensor);
+ graph_output->name("output_" + tname);
+
+ // Set GraphInputOutputIndex for graph
+ output_node->index(graph_output->index());
+
+ // Shape of Output
+ auto output_shape = std::make_unique<loco::TensorShape>();
+ const std::vector<int32_t> &output_dims = tensor.shape; // in NHWC
+ output_shape->rank(output_dims.size());
+ for (uint32_t r = 0; r < output_dims.size(); ++r)
+ output_shape->dim(r) = loco::Dimension(output_dims[r]);
+ graph_output->shape(std::move(output_shape));
+
+ // Data type
+ auto dtype = luci::luci_datatype(tensor.type);
+ graph_output->dtype(dtype);
+ }
+}
+
+class ValidateCollector final : public loco::ErrorListener
+{
+public:
+ void notify(const loco::ErrorDetail<loco::ErrorCategory::MissingArgument> &d) override
+ {
+ LOGGER(l);
+ INFO(l) << "[luci] GraphValidate error " << d.node() << "(" << d.index() << ")" << std::endl;
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+Importer::Importer()
+{
+ // DO NOTHING
+}
+
+std::unique_ptr<loco::Graph> Importer::import(const circle::Model *model) const
+{
+ auto graph = loco::make_graph();
+
+ const GraphBuilderSource *source_ptr = &GraphBuilderRegistry::get();
+
+ if (_source != nullptr)
+ {
+ // Use user-defined GraphBuilderSource
+ source_ptr = _source;
+ }
+
+ CircleReader reader;
+ if (!reader.parse(model))
+ return nullptr;
+
+ // TODO support multiple subgraph when Circle supports
+ assert(reader.num_subgraph() == 1);
+ if (!reader.select_subgraph(0))
+ return nullptr;
+
+ // Convert circle::Model to loco::Graph
+ convert_graph(*source_ptr, reader, graph.get());
+
+ LOGGER(l);
+ INFO(l) << fmt(graph.get());
+
+ assert(loco::valid(graph.get(), std::make_unique<ValidateCollector>()));
+
+ return std::move(graph);
+}
+
+std::unique_ptr<Module> Importer::importModule(const circle::Model *model) const
+{
+ auto module = make_module();
+
+ const GraphBuilderSource *source_ptr = &GraphBuilderRegistry::get();
+
+ if (_source != nullptr)
+ {
+ // Use user-defined GraphBuilderSource
+ source_ptr = _source;
+ }
+
+ CircleReader reader;
+ if (!reader.parse(model))
+ return nullptr;
+
+ for (uint32_t g = 0; g < reader.num_subgraph(); ++g)
+ {
+ auto graph = loco::make_graph();
+
+ if (!reader.select_subgraph(g))
+ return nullptr;
+
+ graph->name(reader.name());
+
+ // Convert circle::Model to loco::Graph
+ convert_graph(*source_ptr, reader, graph.get());
+
+ LOGGER(l);
+ INFO(l) << fmt(graph.get());
+
+ assert(loco::valid(graph.get(), std::make_unique<ValidateCollector>()));
+
+ module->add(std::move(graph));
+ }
+
+ return std::move(module);
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Importer.test.cpp b/compiler/luci/import/src/Importer.test.cpp
new file mode 100644
index 000000000..4426e15fd
--- /dev/null
+++ b/compiler/luci/import/src/Importer.test.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 "luci/Importer.h"
+
+#include <loco.h>
+
+#include <gtest/gtest.h>
+
+TEST(TensorFlowLiteImport, Dummy) { luci::Importer import; }
diff --git a/compiler/luci/import/src/Nodes/CircleAbs.cpp b/compiler/luci/import/src/Nodes/CircleAbs.cpp
new file mode 100644
index 000000000..9054986bd
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAbs.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 "luci/Import/Nodes/CircleAbs.h"
+
+#include <luci/IR/Nodes/CircleAbs.h>
+
+#include <loco.h>
+
+namespace luci
+{
+bool CircleAbsGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO Support type check
+ return true;
+}
+
+CircleNode *CircleAbsGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAbs>();
+ node->x(inputs[0]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleAdd.cpp b/compiler/luci/import/src/Nodes/CircleAdd.cpp
new file mode 100644
index 000000000..3b1bb734f
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAdd.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "luci/Import/Nodes/CircleAdd.h"
+
+#include <luci/IR/Nodes/CircleAdd.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleAddGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleAddGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAdd>();
+ node->x(inputs[0]);
+ node->y(inputs[1]);
+
+ const auto *options = op.builtin_options.AsAddOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleArgMax.cpp b/compiler/luci/import/src/Nodes/CircleArgMax.cpp
new file mode 100644
index 000000000..2679827e2
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleArgMax.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "luci/Import/Nodes/CircleArgMax.h"
+
+#include <luci/IR/Nodes/CircleArgMax.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleArgMaxGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleArgMaxGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleArgMax>();
+ node->input(inputs[0]);
+ node->dimension(inputs[1]);
+
+ const auto *options = op.builtin_options.AsArgMaxOptions();
+ node->output_type(luci_datatype(options->output_type));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp
new file mode 100644
index 000000000..cfc3cf126
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleAveragePool2D.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "luci/Import/Nodes/CircleAveragePool2D.h"
+
+#include <luci/IR/Nodes/CircleAveragePool2D.h>
+
+namespace luci
+{
+
+bool CircleAveragePool2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleAveragePool2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAveragePool2D>();
+ node->value(inputs[0]);
+
+ const auto *options = op.builtin_options.AsPool2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->filter()->w(options->filter_width);
+ node->filter()->h(options->filter_height);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp b/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp
new file mode 100644
index 000000000..4bbfadf64
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleBatchToSpaceND.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "luci/Import/Nodes/CircleBatchToSpaceND.h"
+
+#include <luci/IR/Nodes/CircleBatchToSpaceND.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleBatchToSpaceNDGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 3)
+ return false;
+
+ // input 1 and 2 should have INT32/INT64 type
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor_1 = tensors.at(inputs[1]);
+ switch (tensor_1->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+ const auto &tensor_2 = tensors.at(inputs[2]);
+ switch (tensor_2->type)
+ {
+ case circle::TensorType_INT32:
+ case circle::TensorType_INT64:
+ break;
+ default:
+ return false;
+ }
+
+ // Only support input shape dimension 3 and 4 only
+ const auto &tensor_0 = tensors.at(inputs[0]);
+ const auto t_0_s = tensor_0->shape.size();
+ if (t_0_s != 3 && t_0_s != 4)
+ return false;
+
+ // TODO check input shape
+
+ return true;
+}
+
+CircleNode *CircleBatchToSpaceNDGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleBatchToSpaceND>();
+ node->input(inputs[0]);
+ node->block_shape(inputs[1]);
+ node->crops(inputs[2]);
+
+ // No options for BatchToSpaceND
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleConcatenation.cpp b/compiler/luci/import/src/Nodes/CircleConcatenation.cpp
new file mode 100644
index 000000000..7fc616aa0
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleConcatenation.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "luci/Import/Nodes/CircleConcatenation.h"
+
+#include <luci/IR/Nodes/CircleConcatenation.h>
+
+namespace luci
+{
+
+bool CircleConcatenationGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() < 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleConcatenationGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleConcatenation>(inputs.size());
+ for (uint32_t i = 0; i < inputs.size(); ++i)
+ {
+ node->values(i, inputs[i]);
+ }
+
+ const auto *options = op.builtin_options.AsConcatenationOptions();
+ node->axis(options->axis);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleConst.cpp b/compiler/luci/import/src/Nodes/CircleConst.cpp
new file mode 100644
index 000000000..1d798983b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleConst.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "luci/Import/Nodes/CircleConst.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/Log.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+template <loco::DataType DT>
+static void copy_data(const std::vector<uint8_t> &raw_data, uint32_t num_elements,
+ CircleConst *const_node)
+{
+ using T = typename loco::DataTypeImpl<DT>::Type;
+
+ assert(raw_data.size() == num_elements * sizeof(T));
+ const auto *data = reinterpret_cast<const T *>(raw_data.data());
+
+ const_node->size<DT>(num_elements);
+ for (uint32_t i = 0; i < num_elements; ++i)
+ {
+ const_node->at<DT>(i) = data[i];
+ }
+}
+
+//
+// circleconst_from_tensor() ?
+//
+CircleConst *create_circleconst(GraphBuilderContext *context, int32_t tensor_index)
+{
+ LOGGER(l);
+
+ auto graph = context->graph();
+ auto reader = context->reader();
+ const auto &tensors = reader->tensors();
+
+ // (1) create CircleConst
+ auto const_node = graph->nodes()->create<CircleConst>();
+ const circle::TensorT &const_tensor = *tensors[tensor_index];
+ const_node->name(tensor_name(const_tensor));
+ auto quantization = luci::tensor_quantization(const_tensor);
+ if (quantization)
+ {
+ auto quantparam = luci::luci_quantparam(quantization);
+ if (quantparam.get())
+ const_node->quantparam(std::move(quantparam));
+ }
+
+ INFO(l) << "[luci] NodeFinder const_node(" << tensor_index << ") -> " << const_node << std::endl;
+
+ // (2) set data_type to CircleConst
+ const_node->dtype(luci_datatype(const_tensor.type));
+
+ // (3) set shape to CicleConst
+ std::vector<int32_t> const_dims = const_tensor.shape; // in NHWC
+ const_node->rank(const_dims.size());
+ uint32_t num_elements = 1;
+ for (uint32_t r = 0; r < const_dims.size(); ++r)
+ {
+ const_node->dim(r) = loco::Dimension(const_dims[r]);
+ num_elements = num_elements * const_dims[r];
+ }
+
+ // (4) constant values from circle buffer
+ const std::vector<uint8_t> &buffer = reader->buffers()[const_tensor.buffer]->data;
+ if (buffer.empty())
+ throw oops::UserExn("Empty buffer");
+
+ switch (luci_datatype(const_tensor.type))
+ {
+ case loco::DataType::FLOAT32:
+ copy_data<loco::DataType::FLOAT32>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::U8:
+ copy_data<loco::DataType::U8>(buffer, num_elements, const_node);
+ break;
+
+ case loco::DataType::S32:
+ copy_data<loco::DataType::S32>(buffer, num_elements, const_node);
+ break;
+
+ default:
+ throw oops::UserExn("Unsupported tensor type", circle::EnumNameTensorType(const_tensor.type));
+ }
+
+ return const_node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleConv2D.cpp b/compiler/luci/import/src/Nodes/CircleConv2D.cpp
new file mode 100644
index 000000000..ec9dce0d2
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleConv2D.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "luci/Import/Nodes/CircleConv2D.h"
+
+#include <luci/IR/Nodes/CircleConv2D.h>
+
+#include <loco.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+bool CircleConv2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ // Circle Conv2D may not have a bias but we won't support this
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleConv2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleConv2D>();
+ node->input(inputs[0]);
+ node->filter(inputs[1]);
+ // For now, bias is required (checked in `verify` method).
+ assert(inputs.size() == 3);
+ node->bias(inputs[2]);
+
+ const auto *options = op.builtin_options.AsConv2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ // FIXME Check dilation_w_factor, dilation_h_factor.
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleCos.cpp b/compiler/luci/import/src/Nodes/CircleCos.cpp
new file mode 100644
index 000000000..5f61cc7f6
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleCos.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "luci/Import/Nodes/CircleCos.h"
+
+#include <luci/IR/Nodes/CircleCos.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleCosGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleCosGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleCos>();
+ node->x(inputs[0]);
+
+ // No options for Cos
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp
new file mode 100644
index 000000000..c6d3b1f1e
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDepthwiseConv2D.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "luci/Import/Nodes/CircleDepthwiseConv2D.h"
+
+#include <luci/IR/Nodes/CircleDepthwiseConv2D.h>
+
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleDepthwiseConv2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ // Circle DepthwiseConv2D may not have a bias but we won't support this
+ if (args.op.inputs.size() != 3 && args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDepthwiseConv2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleDepthwiseConv2D>();
+ node->input(inputs[0]);
+ node->filter(inputs[1]);
+ if (inputs.size() != 3)
+ throw oops::UserExn("DepthwiseConv2d without bias is unsupported");
+ node->bias(inputs[2]);
+
+ const auto *options = op.builtin_options.AsDepthwiseConv2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->depthMultiplier(options->depth_multiplier);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ // FIXME Check dilation_w_factor, dilation_h_factor.
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleDiv.cpp b/compiler/luci/import/src/Nodes/CircleDiv.cpp
new file mode 100644
index 000000000..d09cfb815
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleDiv.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "luci/Import/Nodes/CircleDiv.h"
+
+#include <luci/IR/Nodes/CircleDiv.h>
+
+namespace luci
+{
+
+bool CircleDivGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleDivGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto node = graph->nodes()->create<CircleDiv>();
+ node->x(inputs[0]);
+ node->y(inputs[1]);
+
+ const auto *options = op.builtin_options.AsDivOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleEqual.cpp b/compiler/luci/import/src/Nodes/CircleEqual.cpp
new file mode 100644
index 000000000..a53f6e94b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleEqual.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "luci/Import/Nodes/CircleEqual.h"
+
+#include <luci/IR/Nodes/CircleEqual.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleEqualGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+
+ if (inputs.size() != 2)
+ {
+ return false;
+ }
+
+ const auto &tensors = args.reader.tensors();
+
+ return tensors[inputs[0]]->type == tensors[inputs[1]]->type;
+}
+
+CircleNode *CircleEqualGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleEqual>();
+ node->x(inputs[0]);
+ node->y(inputs[1]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleExp.cpp b/compiler/luci/import/src/Nodes/CircleExp.cpp
new file mode 100644
index 000000000..44fc93d09
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleExp.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "luci/Import/Nodes/CircleExp.h"
+
+#include <luci/IR/Nodes/CircleAbs.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleExpGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+
+ // input type check
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs[0]);
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_FLOAT64:
+ break;
+ // TODO support TensorType_COMPLEX64, complex128, bfloat16
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleExpGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleAbs>();
+ node->x(inputs[0]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp
new file mode 100644
index 000000000..8f74fe9ce
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleFullyConnected.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "luci/Import/Nodes/CircleFullyConnected.h"
+
+#include <luci/IR/Nodes/CircleFullyConnected.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CircleFullyConnectedGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 3)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleFullyConnectedGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleFullyConnected>();
+ node->input(inputs[0]);
+ node->weights(inputs[1]);
+ node->bias(inputs[2]);
+
+ const auto *options = op.builtin_options.AsFullyConnectedOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+ if (options->weights_format != circle::FullyConnectedOptionsWeightsFormat_DEFAULT)
+ {
+ throw oops::UserExn(
+ "Unsupported weights format",
+ circle::EnumNameFullyConnectedOptionsWeightsFormat(options->weights_format));
+ }
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp
new file mode 100644
index 000000000..b1ed3ea37
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogicalNot.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "luci/Import/Nodes/CircleLogicalNot.h"
+
+#include <luci/IR/Nodes/CircleLogicalNot.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogicalNotGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // Only BOOL type is allowed for the input
+ const auto &inputs = args.op.inputs;
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs[0]);
+ if (tensor->type != circle::TensorType::TensorType_BOOL)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleLogicalNotGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogicalNot>();
+ node->x(inputs[0]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp
new file mode 100644
index 000000000..00eb9c5df
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleLogicalOr.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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 "luci/Import/Nodes/CircleLogicalOr.h"
+
+#include <luci/IR/Nodes/CircleLogicalOr.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleLogicalOrGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ // Only BOOL type is allowed for inputs
+ const auto &inputs = args.op.inputs;
+ const auto &tensors = args.reader.tensors();
+ for (auto input : inputs)
+ {
+ const auto &tensor = tensors.at(input);
+ if (tensor->type != circle::TensorType::TensorType_BOOL)
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleLogicalOrGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleLogicalOr>();
+ node->x(inputs[0]);
+ node->y(inputs[1]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp
new file mode 100644
index 000000000..1798819cf
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMaxPool2D.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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 "luci/Import/Nodes/CircleMaxPool2D.h"
+
+#include <luci/IR/Nodes/CircleMaxPool2D.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleMaxPool2DGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMaxPool2DGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMaxPool2D>();
+ node->value(inputs[0]);
+
+ const auto *options = op.builtin_options.AsPool2DOptions();
+ node->padding(luci_padding(options->padding));
+ node->stride()->w(options->stride_w);
+ node->stride()->h(options->stride_h);
+ node->filter()->w(options->filter_width);
+ node->filter()->h(options->filter_height);
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMean.cpp b/compiler/luci/import/src/Nodes/CircleMean.cpp
new file mode 100644
index 000000000..8261c7b38
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMean.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "luci/Import/Nodes/CircleMean.h"
+
+#include <luci/IR/Nodes/CircleMean.h>
+
+namespace luci
+{
+
+bool CircleMeanGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMeanGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMean>();
+ node->input(inputs[0]);
+ node->reduction_indices(inputs[1]);
+
+ const auto *options = op.builtin_options.AsReducerOptions();
+ node->keep_dims(options->keep_dims);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleMul.cpp b/compiler/luci/import/src/Nodes/CircleMul.cpp
new file mode 100644
index 000000000..d4412b96b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleMul.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "luci/Import/Nodes/CircleMul.h"
+
+#include <luci/IR/Nodes/CircleMul.h>
+
+namespace luci
+{
+
+bool CircleMulGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleMulGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleMul>();
+ node->x(inputs[0]);
+ node->y(inputs[1]);
+
+ const auto *options = op.builtin_options.AsMulOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePack.cpp b/compiler/luci/import/src/Nodes/CirclePack.cpp
new file mode 100644
index 000000000..6ba6fae11
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePack.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "luci/Import/Nodes/CirclePack.h"
+
+#include <luci/IR/Nodes/CirclePack.h>
+
+#include <loco.h>
+#include <oops/UserExn.h>
+
+namespace luci
+{
+
+bool CirclePackGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ const auto &outputs = args.op.outputs;
+ const auto *options = args.op.builtin_options.AsPackOptions();
+
+ if (options->values_count < 1)
+ return false;
+
+ if (inputs.size() != static_cast<uint32_t>(options->values_count))
+ return false;
+
+ if (outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CirclePackGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePack>(inputs.size());
+ for (uint32_t i = 0; i < inputs.size(); ++i)
+ {
+ node->values(i, inputs[i]);
+ }
+
+ const auto *options = op.builtin_options.AsPackOptions();
+ node->axis(options->axis);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CirclePad.cpp b/compiler/luci/import/src/Nodes/CirclePad.cpp
new file mode 100644
index 000000000..6abcf2d6c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CirclePad.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "luci/Import/Nodes/CirclePad.h"
+
+#include <luci/IR/Nodes/CirclePad.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CirclePadGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CirclePadGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CirclePad>();
+ node->input(inputs[0]);
+ node->paddings(inputs[1]);
+
+ const auto *options = op.builtin_options.AsPadOptions();
+ (void)options; // There are no options.
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRelu.cpp b/compiler/luci/import/src/Nodes/CircleRelu.cpp
new file mode 100644
index 000000000..056268a5b
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRelu.cpp
@@ -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.
+ */
+
+#include "luci/Import/Nodes/CircleRelu.h"
+
+#include <luci/IR/Nodes/CircleRelu.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleReluGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleReluGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRelu>();
+ node->features(inputs[0]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleReshape.cpp b/compiler/luci/import/src/Nodes/CircleReshape.cpp
new file mode 100644
index 000000000..c83f143a6
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleReshape.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "luci/Import/Nodes/CircleReshape.h"
+
+#include <luci/IR/Nodes/CircleConst.h>
+#include <luci/IR/Nodes/CircleReshape.h>
+
+namespace luci
+{
+
+bool CircleReshapeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1 && args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+static void setup_shape_attribute(const std::vector<int32_t> &shape, CircleReshape *node)
+{
+ node->newShape()->rank(shape.size());
+ for (uint32_t i = 0; i < shape.size(); ++i)
+ {
+ node->newShape()->dim(i) = shape[i];
+ }
+}
+
+static CircleNode *create_shape_node(const std::vector<int32_t> &shape, loco::Graph *graph)
+{
+ auto *shape_node = graph->nodes()->create<luci::CircleConst>();
+ shape_node->dtype(loco::DataType::S32);
+ shape_node->rank(1);
+ shape_node->dim(0) = shape.size();
+ shape_node->size<loco::DataType::S32>(shape.size());
+ for (uint32_t i = 0; i < shape.size(); ++i)
+ {
+ shape_node->at<loco::DataType::S32>(i) = shape[i];
+ }
+ return shape_node;
+}
+
+CircleNode *CircleReshapeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ // If the second input is not provided, generate it based on the value of the attribute.
+ // TODO Presence of the second input is the current requirement of the IR.
+ auto *shape_node = (inputs.size() == 2) ? inputs[1] : nullptr;
+ if (shape_node == nullptr)
+ {
+ const auto *options = op.builtin_options.AsReshapeOptions();
+ shape_node = create_shape_node(options->new_shape, graph);
+ }
+
+ auto *node = graph->nodes()->create<CircleReshape>();
+ node->tensor(inputs[0]);
+ node->shape(shape_node);
+
+ const auto *options = op.builtin_options.AsReshapeOptions();
+ setup_shape_attribute(options->new_shape, node);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleRsqrt.cpp b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp
new file mode 100644
index 000000000..b5de0b575
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleRsqrt.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "luci/Import/Nodes/CircleRsqrt.h"
+
+#include <luci/IR/Nodes/CircleRsqrt.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleRsqrtGraphBuilder::validate(const ValidateArgs &args) const
+{
+ const auto &inputs = args.op.inputs;
+ if (inputs.size() != 1)
+ return false;
+
+ // Must be one of the following types
+ // bfloat16, half (float16), float32, float64, complex64, complex128
+ // Currently, circle supports float16, float32, complex64
+ const auto &tensors = args.reader.tensors();
+ const auto &tensor = tensors.at(inputs[0]);
+ switch (tensor->type)
+ {
+ case circle::TensorType_FLOAT16:
+ case circle::TensorType_FLOAT32:
+ case circle::TensorType_COMPLEX64:
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+CircleNode *CircleRsqrtGraphBuilder::build_node(const circle::OperatorT &,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleRsqrt>();
+ node->x(inputs[0]);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSoftmax.cpp b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp
new file mode 100644
index 000000000..0d316e18c
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSoftmax.cpp
@@ -0,0 +1,49 @@
+/*
+ * 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 "luci/Import/Nodes/CircleSoftmax.h"
+
+#include <luci/IR/Nodes/CircleSoftmax.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSoftmaxGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 1)
+ return false;
+
+ // TODO do attribute checks
+
+ return true;
+}
+
+CircleNode *CircleSoftmaxGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSoftmax>();
+ node->logits(inputs[0]);
+
+ const auto *options = op.builtin_options.AsSoftmaxOptions();
+ node->beta(options->beta);
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleSub.cpp b/compiler/luci/import/src/Nodes/CircleSub.cpp
new file mode 100644
index 000000000..968e9f51f
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleSub.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "luci/Import/Nodes/CircleSub.h"
+
+#include <luci/IR/Nodes/CircleSub.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleSubGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleSubGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleSub>();
+ node->x(inputs[0]);
+ node->y(inputs[1]);
+
+ const auto *options = op.builtin_options.AsSubOptions();
+ node->fusedActivationFunction(luci_actfunc(options->fused_activation_function));
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/import/src/Nodes/CircleTranspose.cpp b/compiler/luci/import/src/Nodes/CircleTranspose.cpp
new file mode 100644
index 000000000..8622c8b80
--- /dev/null
+++ b/compiler/luci/import/src/Nodes/CircleTranspose.cpp
@@ -0,0 +1,51 @@
+/*
+ * 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 "luci/Import/Nodes/CircleTranspose.h"
+
+#include <luci/IR/Nodes/CircleTranspose.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool CircleTransposeGraphBuilder::validate(const ValidateArgs &args) const
+{
+ if (args.op.inputs.size() != 2)
+ return false;
+
+ if (args.op.outputs.size() != 1)
+ return false;
+
+ return true;
+}
+
+CircleNode *CircleTransposeGraphBuilder::build_node(const circle::OperatorT &op,
+ const std::vector<CircleNode *> &inputs,
+ loco::Graph *graph) const
+{
+ auto *node = graph->nodes()->create<CircleTranspose>();
+ node->a(inputs[0]);
+ node->perm(inputs[1]);
+
+ const auto *options = op.builtin_options.AsTransposeOptions();
+ (void)options;
+
+ return node;
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/CMakeLists.txt b/compiler/luci/lang/CMakeLists.txt
new file mode 100644
index 000000000..564e777fb
--- /dev/null
+++ b/compiler/luci/lang/CMakeLists.txt
@@ -0,0 +1,22 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_lang SHARED ${SOURCES})
+target_include_directories(luci_lang PRIVATE src)
+target_include_directories(luci_lang PUBLIC include)
+target_link_libraries(luci_lang PUBLIC loco)
+target_link_libraries(luci_lang PUBLIC oops)
+target_link_libraries(luci_lang PRIVATE nncc_common)
+
+install(TARGETS luci_lang DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_lang_test ${TESTS})
+target_include_directories(luci_lang_test PRIVATE src)
+target_link_libraries(luci_lang_test luci_lang)
diff --git a/compiler/luci/lang/README.md b/compiler/luci/lang/README.md
new file mode 100644
index 000000000..ea0e3d5da
--- /dev/null
+++ b/compiler/luci/lang/README.md
@@ -0,0 +1,3 @@
+# luci-lang
+
+`luci-lang` provides TensorFlow Lite and Circle Dialect IR
diff --git a/compiler/luci/lang/include/luci/IR/AttrFilter.h b/compiler/luci/lang/include/luci/IR/AttrFilter.h
new file mode 100644
index 000000000..7909fa523
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrFilter.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __LUCI_IR_ATTRFILTER_H__
+#define __LUCI_IR_ATTRFILTER_H__
+
+#include <stdint.h>
+
+namespace luci
+{
+
+class Filter final
+{
+public:
+ Filter() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRFILTER_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
new file mode 100644
index 000000000..2abae604b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrFusedActFunc.h
@@ -0,0 +1,36 @@
+/*
+ * 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 __LUCI_IR_ATTRFUSEDACTFUNC_H__
+#define __LUCI_IR_ATTRFUSEDACTFUNC_H__
+
+namespace luci
+{
+
+// TODO Divide into TFL version and Circle version when they go different approach
+enum class FusedActFunc
+{
+ UNDEFINED, // This is not defined by TFLite or Circle. This was added to
+ // prevent programming error.
+ NONE,
+ RELU,
+ RELU_N1_TO_1,
+ RELU6
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRFUSEDACTFUNC_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrPadding.h b/compiler/luci/lang/include/luci/IR/AttrPadding.h
new file mode 100644
index 000000000..5c295e0cd
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrPadding.h
@@ -0,0 +1,33 @@
+/*
+ * 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 __LUCI_IR_ATTRPADDING_H__
+#define __LUCI_IR_ATTRPADDING_H__
+
+namespace luci
+{
+
+enum class Padding
+{
+ UNDEFINED, // This is not defined by TFLite. This was added to prevent programming error.
+
+ SAME,
+ VALID,
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRPADDING_H__
diff --git a/compiler/luci/lang/include/luci/IR/AttrStride.h b/compiler/luci/lang/include/luci/IR/AttrStride.h
new file mode 100644
index 000000000..654967d73
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/AttrStride.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __LUCI_IR_ATTRSTRIDE_H__
+#define __LUCI_IR_ATTRSTRIDE_H__
+
+#include <stdint.h>
+
+namespace luci
+{
+
+class Stride final
+{
+public:
+ Stride() : _w(1), _h(1) {}
+
+ int32_t w() const { return _w; }
+ void w(int32_t w) { _w = w; }
+
+ int32_t h() const { return _h; }
+ void h(int32_t h) { _h = h; }
+
+private:
+ int32_t _w;
+ int32_t _h;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_ATTRSTRIDE_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleDialect.h b/compiler/luci/lang/include/luci/IR/CircleDialect.h
new file mode 100644
index 000000000..1b25dc9c2
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleDialect.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __LUCI_IR_CIRCLEDIALECT_H__
+#define __LUCI_IR_CIRCLEDIALECT_H__
+
+#include <loco/IR/Dialect.h>
+
+namespace luci
+{
+
+/**
+ * @brief A singleton for Circle Dialect
+ */
+class CircleDialect final : public loco::Dialect
+{
+private:
+ CircleDialect();
+
+public:
+ CircleDialect(const CircleDialect &) = delete;
+ CircleDialect(CircleDialect &&) = delete;
+
+public:
+ static loco::Dialect *get(void);
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEDIALECT_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNode.h b/compiler/luci/lang/include/luci/IR/CircleNode.h
new file mode 100644
index 000000000..92816ef04
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNode.h
@@ -0,0 +1,23 @@
+/*
+ * 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 __LUCI_IR_CIRCLENODE_H__
+#define __LUCI_IR_CIRCLENODE_H__
+
+#include "CircleNodeDecl.h"
+#include "CircleNodeImpl.h"
+
+#endif // __LUCI_IR_CIRCLENODE_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
new file mode 100644
index 000000000..b87bdf9d0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeDecl.h
@@ -0,0 +1,68 @@
+/*
+ * 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 __LUCI_IR_CIRCLENODEDECL_H__
+#define __LUCI_IR_CIRCLENODEDECL_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Dialect.h>
+
+#include "CircleOpcode.h"
+#include "CircleNodeVisitor.forward.h"
+#include "CircleQuantParam.h"
+
+#include <memory>
+
+namespace luci
+{
+
+using NodeName = std::string;
+
+struct CircleNode : public loco::Node
+{
+ virtual ~CircleNode() = default;
+
+ const loco::Dialect *dialect(void) const final;
+ virtual CircleOpcode opcode(void) const = 0;
+
+ template <typename T> T accept(CircleNodeVisitorBase<T> *) const;
+ template <typename T> T accept(CircleNodeMutableVisitorBase<T> *);
+
+ NodeName name(void) const { return _name; }
+ void name(const NodeName &name) { _name = name; }
+
+ CircleQuantParam *quantparam(void) const { return _quantparam.get(); }
+ void quantparam(std::unique_ptr<CircleQuantParam> &&quantparam)
+ {
+ _quantparam = std::move(quantparam);
+ }
+
+private:
+ NodeName _name;
+ std::unique_ptr<CircleQuantParam> _quantparam;
+};
+
+template <CircleOpcode Code> struct CircleNodeImpl : public CircleNode
+{
+ virtual ~CircleNodeImpl() = default;
+
+ uint32_t opnum(void) const final { return static_cast<uint32_t>(Code); }
+ CircleOpcode opcode(void) const final { return Code; }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODEDECL_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h
new file mode 100644
index 000000000..bdcfc9c9d
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeImpl.h
@@ -0,0 +1,70 @@
+/*
+ * 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 __LUCI_IR_CIRCLENODEIMPL_H__
+#define __LUCI_IR_CIRCLENODEIMPL_H__
+
+#include "CircleNodes.h"
+#include "CircleNodeVisitor.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+template <typename T> T CircleNode::accept(CircleNodeVisitorBase<T> *v) const
+{
+ switch (this->opcode())
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ \
+ case CircleOpcode::OPCODE: \
+ return v->visit(dynamic_cast<const CLASS *>(this));
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("CircleNode::accept(CircleNodeVisitorBase) not handled");
+}
+
+template <typename T> T CircleNode::accept(CircleNodeMutableVisitorBase<T> *v)
+{
+ switch (this->opcode())
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ \
+ case CircleOpcode::OPCODE: \
+ return v->visit(dynamic_cast<CLASS *>(this));
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+
+ default:
+ break;
+ }
+
+ INTERNAL_EXN("CircleNode::accept(CircleNodeMutableVisitorBase) not handled");
+}
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODEIMPL_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h
new file mode 100644
index 000000000..70901ca87
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.forward.h
@@ -0,0 +1,30 @@
+/*
+ * 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 __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__
+#define __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__
+
+namespace luci
+{
+
+// NOTE These forward declarations SHOULD BE aligned with Node delcarations in
+// "CircleNodeVisitor.h"
+template <typename T> struct CircleNodeVisitorBase;
+template <typename T> struct CircleNodeMutableVisitorBase;
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODE_VISITOR_FORWARD_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h
new file mode 100644
index 000000000..43339fe84
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodeVisitor.h
@@ -0,0 +1,87 @@
+/*
+ * 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 __LUCI_IR_CIRCLENODE_VISITOR_H__
+#define __LUCI_IR_CIRCLENODE_VISITOR_H__
+
+#include "CircleNode.h"
+#include "CircleNodes.h"
+
+#include <oops/InternalExn.h>
+
+namespace luci
+{
+
+/**
+ * DO NOT use this class. Use CircleNodeVisitor instead.
+ */
+template <typename T> struct CircleNodeVisitorBase
+{
+ virtual ~CircleNodeVisitorBase() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(const CIRCLE_CLASS *) = 0;
+
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+template <typename T> struct CircleNodeVisitor : public CircleNodeVisitorBase<T>
+{
+ virtual ~CircleNodeVisitor() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \
+ virtual T visit(const CIRCLE_CLASS *node) { return visit(static_cast<const CircleNode *>(node)); }
+
+#include "CircleNodes.lst"
+
+#undef CIRCLE_NODE
+
+ /// @brief Default fallback
+ virtual T visit(const CircleNode *) { INTERNAL_EXN("CircleNodeVisitor: NYI node"); }
+};
+
+/**
+ * DO NOT use this class. Use CircleNodeMutableVisitor instead.
+ */
+template <typename T> struct CircleNodeMutableVisitorBase
+{
+ virtual ~CircleNodeMutableVisitorBase() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) virtual T visit(CIRCLE_CLASS *) = 0;
+
+#include "CircleNodes.lst"
+
+#undef CIRCLE_NODE
+};
+
+template <typename T> struct CircleNodeMutableVisitor : public CircleNodeMutableVisitorBase<T>
+{
+ virtual ~CircleNodeMutableVisitor() = default;
+
+#define CIRCLE_NODE(OPCODE, CIRCLE_CLASS) \
+ virtual T visit(CIRCLE_CLASS *node) { return visit(static_cast<CircleNode *>(node)); }
+
+#include "CircleNodes.lst"
+
+#undef CIRCLE_NODE
+
+ /// @brief Default fallback
+ virtual T visit(CircleNode *) { INTERNAL_EXN("CircleNodeMutableVisitor: NYI node"); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CircleNode_VISITOR_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.h b/compiler/luci/lang/include/luci/IR/CircleNodes.h
new file mode 100644
index 000000000..cc822842b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodes.h
@@ -0,0 +1,73 @@
+/*
+ * 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 __LUCI_IR_CIRCLENODES_H__
+#define __LUCI_IR_CIRCLENODES_H__
+
+#include "Nodes/CircleAbs.h"
+#include "Nodes/CircleAdd.h"
+#include "Nodes/CircleArgMax.h"
+#include "Nodes/CircleAveragePool2D.h"
+#include "Nodes/CircleBatchToSpaceND.h"
+#include "Nodes/CircleConcatenation.h"
+#include "Nodes/CircleConst.h"
+#include "Nodes/CircleConv2D.h"
+#include "Nodes/CircleCos.h"
+#include "Nodes/CircleDepthwiseConv2D.h"
+#include "Nodes/CircleDiv.h"
+#include "Nodes/CircleEqual.h"
+#include "Nodes/CircleExp.h"
+#include "Nodes/CircleFullyConnected.h"
+#include "Nodes/CircleGather.h"
+#include "Nodes/CircleLogicalNot.h"
+#include "Nodes/CircleLogicalOr.h"
+#include "Nodes/CircleMaximum.h"
+#include "Nodes/CircleMaxPool2D.h"
+#include "Nodes/CircleMean.h"
+#include "Nodes/CircleMul.h"
+#include "Nodes/CirclePack.h"
+#include "Nodes/CirclePad.h"
+#include "Nodes/CircleRelu6.h"
+#include "Nodes/CircleRelu.h"
+#include "Nodes/CircleReshape.h"
+#include "Nodes/CircleRsqrt.h"
+#include "Nodes/CircleSoftmax.h"
+#include "Nodes/CircleSqrt.h"
+#include "Nodes/CircleSquaredDifference.h"
+#include "Nodes/CircleSub.h"
+#include "Nodes/CircleTransposeConv.h"
+#include "Nodes/CircleTranspose.h"
+// Circle only
+#include "Nodes/CircleInstanceNorm.h"
+// Virtual nodes
+#include "Nodes/CircleInput.h"
+#include "Nodes/CircleOutput.h"
+
+namespace luci
+{
+
+/**
+ * @brief Set both CircleReshape's 2nd input as CircleConst, and newShape attribute
+ * with same value
+ * @note Shape inference for TFLReshape forces them to be same
+ *
+ * TODO find better place for this helper
+ */
+void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size);
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLENODES_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleNodes.lst b/compiler/luci/lang/include/luci/IR/CircleNodes.lst
new file mode 100644
index 000000000..ca3f7fb0f
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleNodes.lst
@@ -0,0 +1,52 @@
+#ifndef CIRCLE_NODE
+#error "Define CIRCLE_NODE"
+#endif // CIRCLE_NODE
+
+//
+// PLEASE SORT NODE DECLS IN ALPHABETICAL ORDER
+//
+// Naming rule: Follow names in TensorFlow C++ source; same as TFDialect
+// ex) for AvgPool, tensorflow/core/ops/nn_ops.cc
+// REGISTER_OP("AvgPool") <-- OPCODE: AvgPool. Prefix `Circle` for CLASS name
+// .Input("value: T") <-- Input name is 'value'
+//
+
+CIRCLE_NODE(ABS, luci::CircleAbs)
+CIRCLE_NODE(ADD, luci::CircleAdd)
+CIRCLE_NODE(ARG_MAX, luci::CircleArgMax)
+CIRCLE_NODE(AVERAGE_POOL_2D, luci::CircleAveragePool2D)
+CIRCLE_NODE(BATCH_TO_SPACE_ND, luci::CircleBatchToSpaceND)
+CIRCLE_NODE(CONCATENATION, luci::CircleConcatenation)
+CIRCLE_NODE(CONST, luci::CircleConst)
+CIRCLE_NODE(CONV_2D, luci::CircleConv2D)
+CIRCLE_NODE(COS, luci::CircleCos)
+CIRCLE_NODE(DEPTHWISE_CONV_2D, luci::CircleDepthwiseConv2D)
+CIRCLE_NODE(DIV, luci::CircleDiv)
+CIRCLE_NODE(EQUAL, luci::CircleEqual)
+CIRCLE_NODE(EXP, luci::CircleExp)
+CIRCLE_NODE(FULLY_CONNECTED, luci::CircleFullyConnected)
+CIRCLE_NODE(GATHER, luci::CircleGather)
+CIRCLE_NODE(LOGICAL_NOT, luci::CircleLogicalNot)
+CIRCLE_NODE(LOGICAL_OR, luci::CircleLogicalOr)
+CIRCLE_NODE(MAXIMUM, luci::CircleMaximum)
+CIRCLE_NODE(MAX_POOL_2D, luci::CircleMaxPool2D)
+CIRCLE_NODE(MEAN, luci::CircleMean)
+CIRCLE_NODE(MUL, luci::CircleMul)
+CIRCLE_NODE(PACK, luci::CirclePack)
+CIRCLE_NODE(PAD, luci::CirclePad)
+CIRCLE_NODE(RELU, luci::CircleRelu)
+CIRCLE_NODE(RELU6, luci::CircleRelu6)
+CIRCLE_NODE(RESHAPE, luci::CircleReshape)
+CIRCLE_NODE(RSQRT, luci::CircleRsqrt)
+CIRCLE_NODE(SOFTMAX, luci::CircleSoftmax)
+CIRCLE_NODE(SQRT, luci::CircleSqrt)
+CIRCLE_NODE(SQUARED_DIFFERENCE, luci::CircleSquaredDifference)
+CIRCLE_NODE(SUB, luci::CircleSub)
+// TODO TFLTanh
+CIRCLE_NODE(TRANSPOSE, luci::CircleTranspose)
+CIRCLE_NODE(TRANSPOSE_CONV, luci::CircleTransposeConv)
+// Circle Only
+CIRCLE_NODE(INSTANCE_NORM, luci::CircleInstanceNorm)
+// Virtual node(s)
+CIRCLE_NODE(CIRCLEINPUT, luci::CircleInput)
+CIRCLE_NODE(CIRCLEOUTPUT, luci::CircleOutput)
diff --git a/compiler/luci/lang/include/luci/IR/CircleOpcode.h b/compiler/luci/lang/include/luci/IR/CircleOpcode.h
new file mode 100644
index 000000000..703b70da2
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleOpcode.h
@@ -0,0 +1,32 @@
+/*
+ * 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 __LUCI_IR_CIRCLEOPCODE_H__
+#define __LUCI_IR_CIRCLEOPCODE_H__
+
+namespace luci
+{
+
+enum class CircleOpcode
+{
+#define CIRCLE_NODE(OPCODE, CLASS) OPCODE,
+#include "CircleNodes.lst"
+#undef CIRCLE_NODE
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEOPCODE_H__
diff --git a/compiler/luci/lang/include/luci/IR/CircleQuantParam.h b/compiler/luci/lang/include/luci/IR/CircleQuantParam.h
new file mode 100644
index 000000000..7253e657b
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/CircleQuantParam.h
@@ -0,0 +1,36 @@
+/*
+ * 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 __LUCI_IR_CIRCLEQUANTPARAM_H__
+#define __LUCI_IR_CIRCLEQUANTPARAM_H__
+
+#include <cstdint>
+#include <vector>
+
+namespace luci
+{
+
+struct CircleQuantParam
+{
+ std::vector<float> min;
+ std::vector<float> max;
+ std::vector<float> scale;
+ std::vector<int64_t> zerop;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEQUANTPARAM_H__
diff --git a/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h
new file mode 100644
index 000000000..b18ac5dc4
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/LuciNodeMixins.h
@@ -0,0 +1,104 @@
+/*
+ * 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 __LUCI_IR_LUCINODEMIXINS_H__
+#define __LUCI_IR_LUCINODEMIXINS_H__
+
+#include "luci/IR/AttrFusedActFunc.h"
+
+#include <loco/IR/Node.h>
+#include <loco/IR/NodeMixins.h>
+
+namespace luci
+{
+
+/// @brief enumeration of mixin class
+enum class LuciNodeTrait
+{
+ FusedActFunc,
+ Bias
+};
+
+template <LuciNodeTrait T> class LuciNodeMixin;
+
+template <> class LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ LuciNodeMixin() = default;
+
+public:
+ FusedActFunc fusedActivationFunction() const { return _fused_act_fun; }
+ void fusedActivationFunction(FusedActFunc fused_act_fun) { _fused_act_fun = fused_act_fun; }
+
+private:
+ FusedActFunc _fused_act_fun = FusedActFunc::UNDEFINED;
+};
+
+/**
+ * @brief Mixin class for nodes that has a bias input
+ */
+template <> class LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ LuciNodeMixin() = default;
+
+public:
+ virtual loco::Node *bias(void) const = 0; /// @brief get the input for bias.
+ virtual void bias(loco::Node *node) = 0; /// @brief set the input for bias.
+};
+
+/**
+ * @brief Nodes with the fixed number of inputs
+ *
+ * TODO Deprecated this class, and use loco::FixedArity instead
+ */
+template <unsigned N, typename Base> class FixedArityNode : public Base
+{
+public:
+ FixedArityNode()
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args[n] = std::make_unique<loco::Use>(this);
+ }
+ }
+
+ virtual ~FixedArityNode() = default;
+
+public:
+ unsigned arity(void) const final { return N; }
+
+ loco::Node *arg(uint32_t n) const final { return _args.at(n)->node(); }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < N; ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(unsigned n) const { return _args.at(n).get(); }
+
+private:
+ std::array<std::unique_ptr<loco::Use>, N> _args;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_LUCINODEMIXINS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Module.h b/compiler/luci/lang/include/luci/IR/Module.h
new file mode 100644
index 000000000..30eac59ce
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Module.h
@@ -0,0 +1,70 @@
+/*
+ * 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 __LUCI_MODULE_H__
+#define __LUCI_MODULE_H__
+
+#include <loco/IR/Graph.h>
+
+#include <memory>
+#include <vector>
+
+namespace luci
+{
+
+/**
+ * @brief Collection of 'loco::Graph's
+ */
+class Module final
+{
+public:
+ Module() = default;
+
+ // Copy/Move is not allowed for Module
+ Module(const Module &) = delete;
+ Module(Module &&) = delete;
+
+ ~Module() = default;
+
+public:
+ size_t size(void) const { return _graphs.size(); }
+
+public:
+ void add(std::unique_ptr<loco::Graph> &&g);
+
+ /**
+ * @brief provide main graph
+ */
+ loco::Graph *graph(void) const;
+
+ /**
+ * @brief provide graph with an index
+ *
+ * @note graph(0) is interpreted as a main graph
+ */
+ loco::Graph *graph(size_t idx) const;
+
+ // TODO provide graph accessor with a name
+
+private:
+ std::vector<std::unique_ptr<loco::Graph>> _graphs;
+};
+
+std::unique_ptr<Module> make_module(void);
+
+} // namespace luci
+
+#endif // __LUCI_MODULE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h
new file mode 100644
index 000000000..45dba15bf
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAbs.h
@@ -0,0 +1,40 @@
+/*
+ * 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 __LUCI_IR_CIRCELABS_H__
+#define __LUCI_IR_CIRCELABS_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ABS in Circle
+ */
+class CircleAbs final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::ABS>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELABS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h
new file mode 100644
index 000000000..f26eccd1a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAdd.h
@@ -0,0 +1,45 @@
+/*
+ * 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 __LUCI_IR_CIRCELADD_H__
+#define __LUCI_IR_CIRCELADD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ADD in Circle
+ */
+class CircleAdd final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ADD>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELADD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h
new file mode 100644
index 000000000..dbc4b2b3a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleArgMax.h
@@ -0,0 +1,50 @@
+/*
+ * 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 __LUCI_IR_CIRCELARGMAX_H__
+#define __LUCI_IR_CIRCELARGMAX_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief ARG_MAX in Circle
+ */
+class CircleArgMax final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::ARG_MAX>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *dimension(void) const { return at(1)->node(); }
+ void dimension(loco::Node *node) { at(1)->node(node); }
+
+public:
+ loco::DataType output_type(void) const { return _output_type; }
+ void output_type(loco::DataType ot) { _output_type = ot; }
+
+private:
+ loco::DataType _output_type{loco::DataType::S64};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCELARGMAX_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h
new file mode 100644
index 000000000..0b43b40c8
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleAveragePool2D.h
@@ -0,0 +1,63 @@
+/*
+ * 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 __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__
+#define __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief AVERAGE_POOL_2D in Circle
+ */
+class CircleAveragePool2D final
+ : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::AVERAGE_POOL_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleAveragePool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEAVERAGEPOOL2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.h
new file mode 100644
index 000000000..67c0a2102
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleBatchToSpaceND.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 __LUCI_IR_CIRCLEBATCHTOSPACEND_H__
+#define __LUCI_IR_CIRCLEBATCHTOSPACEND_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief BATCH_TO_SPACE_ND in Circle
+ */
+class CircleBatchToSpaceND final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::BATCH_TO_SPACE_ND>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *block_shape(void) const { return at(1)->node(); }
+ void block_shape(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *crops(void) const { return at(2)->node(); }
+ void crops(loco::Node *node) { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEBATCHTOSPACEND_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h
new file mode 100644
index 000000000..8a6778a2f
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConcatenation.h
@@ -0,0 +1,72 @@
+/*
+ * 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 __LUCI_IR_CIRCLECONCATENATION_H__
+#define __LUCI_IR_CIRCLECONCATENATION_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+#include "luci/IR/VariadicArityNode.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief CONCATENATION in Circle
+ */
+class CircleConcatenation final
+ : public VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleConcatenation(uint32_t arity)
+ : VariadicArityNode<CircleNodeImpl<CircleOpcode::CONCATENATION>>(arity)
+ {
+ // TODO Support when arity is 0
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t numValues(void) const { return arity(); }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < numValues());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < numValues());
+ at(index)->node(node);
+ }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECONCATENATION_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h
new file mode 100644
index 000000000..089836eb9
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConst.h
@@ -0,0 +1,57 @@
+/*
+ * 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 __LUCI_IR_CIRCLECONST_H__
+#define __LUCI_IR_CIRCLECONST_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <loco/IR/DataTypeTraits.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to build tensor data
+ * @note This will not be exported as a specific op
+ */
+class CircleConst final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CONST>>,
+ public loco::NodeMixin<loco::NodeTrait::DataType>,
+ public loco::NodeMixin<loco::NodeTrait::TensorShape>
+{
+public:
+ CircleConst() = default;
+
+public:
+ template <loco::DataType DT> uint32_t size(void) const;
+ template <loco::DataType DT> void size(uint32_t size);
+ template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &at(uint32_t n) const;
+ template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &at(uint32_t n);
+
+ template <loco::DataType DT> const typename loco::DataTypeImpl<DT>::Type &scalar(void) const;
+ template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &scalar(void);
+
+private:
+ std::vector<uint8_t> _data;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECONST_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h
new file mode 100644
index 000000000..54318e65c
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleConv2D.h
@@ -0,0 +1,62 @@
+/*
+ * 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 __LUCI_IR_CIRCLECONV2D_H__
+#define __LUCI_IR_CIRCLECONV2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief CONV_2D in Circle
+ */
+class CircleConv2D final : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::CONV_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+
+public:
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding = Padding::UNDEFINED;
+ Stride _stride;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLECONV2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h
new file mode 100644
index 000000000..07ced620a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleCos.h
@@ -0,0 +1,40 @@
+/*
+ * 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 __LUCI_IR_CIRCLE_COS_H__
+#define __LUCI_IR_CIRCLE_COS_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief COS in Circle
+ */
+class CircleCos final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::COS>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_COS_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h
new file mode 100644
index 000000000..15ee62ba7
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDepthwiseConv2D.h
@@ -0,0 +1,68 @@
+/*
+ * 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 __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__
+#define __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DEPTHWISE_CONV_2D in Circle
+ */
+class CircleDepthwiseConv2D final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::DEPTHWISE_CONV_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+
+public:
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+ int32_t depthMultiplier(void) const { return _depth_multiplier; }
+ void depthMultiplier(int32_t arg) { _depth_multiplier = arg; }
+
+private:
+ Padding _padding = Padding::UNDEFINED;
+ Stride _stride;
+ int32_t _depth_multiplier = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEDEPTHWISECONV2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h
new file mode 100644
index 000000000..1d4d3a239
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleDiv.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __LUCI_IR_CIRCLEDIV_H__
+#define __LUCI_IR_CIRCLEDIV_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief DIV in Circle
+ */
+class CircleDiv final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::DIV>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleDiv() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEDIV_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h
new file mode 100644
index 000000000..2087d097a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleEqual.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __LUCI_IR_CIRCLE_EQUAL_H__
+#define __LUCI_IR_CIRCLE_EQUAL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief EQUAL in Circle
+ */
+class CircleEqual final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::EQUAL>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_EQUAL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h
new file mode 100644
index 000000000..97aecb30a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleExp.h
@@ -0,0 +1,40 @@
+/*
+ * 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 __LUCI_IR_CIRCLE_EXP_H__
+#define __LUCI_IR_CIRCLE_EXP_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief EXP in Circle
+ */
+class CircleExp final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::EXP>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_EXP_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h
new file mode 100644
index 000000000..d78f39494
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleFullyConnected.h
@@ -0,0 +1,50 @@
+/*
+ * 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 __LUCI_IR_CIRCLEFULLYCONNECTED_H__
+#define __LUCI_IR_CIRCLEFULLYCONNECTED_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief FULLY_CONNECTED in Circle
+ */
+class CircleFullyConnected final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::FULLY_CONNECTED>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>,
+ public LuciNodeMixin<LuciNodeTrait::Bias>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *weights(void) const { return at(1)->node(); }
+ void weights(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *bias(void) const override { return at(2)->node(); }
+ void bias(loco::Node *node) override { at(2)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEFULLYCONNECTED_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h
new file mode 100644
index 000000000..489596c04
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleGather.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __LUCI_IR_CIRCLEGATHER_H__
+#define __LUCI_IR_CIRCLEGATHER_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief GATHER in Circle
+ */
+class CircleGather final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::GATHER>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *positions(void) const { return at(1)->node(); }
+ void positions(loco::Node *node) { at(1)->node(node); }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis = 0;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEGATHER_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h
new file mode 100644
index 000000000..2c4d60253
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInput.h
@@ -0,0 +1,55 @@
+/*
+ * 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 __LUCI_IR_CIRCLEINPUT_H__
+#define __LUCI_IR_CIRCLEINPUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <loco/IR/DataTypeTraits.h>
+#include <loco/IR/GraphInputIndex.h>
+
+namespace luci
+{
+
+/**
+ * @brief CircleNode used for Input of the Graph
+ * @note This will not be exported as a specific op
+ */
+class CircleInput final : public FixedArityNode<0, CircleNodeImpl<CircleOpcode::CIRCLEINPUT>>,
+ public loco::NodeMixin<loco::NodeTrait::DataType>,
+ public loco::NodeMixin<loco::NodeTrait::TensorShape>
+{
+public:
+ CircleInput() = default;
+
+public:
+ void index(const loco::GraphInputIndex &index);
+ loco::GraphInputIndex index(void) const;
+
+ bool indexed(void) const { return _index != -1; }
+
+private:
+ int64_t _index = -1; // Uninitialized
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEINPUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h
new file mode 100644
index 000000000..db0faa05e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleInstanceNorm.h
@@ -0,0 +1,56 @@
+/*
+ * 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 __LUCI_IR_CIRCLEINSTANCENORM_H__
+#define __LUCI_IR_CIRCLEINSTANCENORM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief INSTANCE_NORM in Circle
+ */
+class CircleInstanceNorm final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::INSTANCE_NORM>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ /// @note Currently only support FLOAT32 as input node
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *gamma(void) const { return at(1)->node(); }
+ void gamma(loco::Node *node) { at(1)->node(node); }
+
+ loco::Node *beta(void) const { return at(2)->node(); }
+ void beta(loco::Node *node) { at(2)->node(node); }
+
+ float epsilon() const { return _epsilon; }
+ void epsilon(float epsilon) { _epsilon = epsilon; }
+
+private:
+ float _epsilon = 1e-05;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEINSTANCENORM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h
new file mode 100644
index 000000000..749dbe518
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalNot.h
@@ -0,0 +1,40 @@
+/*
+ * 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 __LUCI_IR_CIRCLE_LOGICALNOT_H__
+#define __LUCI_IR_CIRCLE_LOGICALNOT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOGICAL_NOT in Circle
+ */
+class CircleLogicalNot final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::LOGICAL_NOT>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOGICALNOT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h
new file mode 100644
index 000000000..570be57af
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleLogicalOr.h
@@ -0,0 +1,43 @@
+/*
+ * 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 __LUCI_IR_CIRCLE_LOGICALOR_H__
+#define __LUCI_IR_CIRCLE_LOGICALOR_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief LOGICAL_OR in Circle
+ */
+class CircleLogicalOr final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::LOGICAL_OR>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLE_LOGICALOR_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h
new file mode 100644
index 000000000..1eb6532ff
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaxPool2D.h
@@ -0,0 +1,62 @@
+/*
+ * 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 __LUCI_IR_CIRCLEMAXPOOL2D_H__
+#define __LUCI_IR_CIRCLEMAXPOOL2D_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFilter.h"
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MAX_POOL_2D in Circle
+ */
+class CircleMaxPool2D final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::MAX_POOL_2D>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleMaxPool2D() : _padding(Padding::UNDEFINED) { /* empty */}
+
+public:
+ loco::Node *value(void) const { return at(0)->node(); }
+ void value(loco::Node *node) { at(0)->node(node); }
+
+ Padding padding() const { return _padding; }
+ void padding(Padding padding) { _padding = padding; }
+
+ const Filter *filter(void) const { return &_filter; }
+ Filter *filter(void) { return &_filter; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+ Filter _filter;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMAXPOOL2D_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h
new file mode 100644
index 000000000..cf7305e3a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMaximum.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __LUCI_IR_CIRCLEMAXIMUM_H__
+#define __LUCI_IR_CIRCLEMAXIMUM_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MAXIMUM in Circle
+ */
+class CircleMaximum final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MAXIMUM>>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMAXIMUM_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h
new file mode 100644
index 000000000..6fd791450
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMean.h
@@ -0,0 +1,51 @@
+/*
+ * 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 __LUCI_IR_CIRCLEMEAN_H__
+#define __LUCI_IR_CIRCLEMEAN_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MEAN in Circle
+ */
+class CircleMean final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MEAN>>
+{
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *reduction_indices(void) const { return at(1)->node(); }
+ void reduction_indices(loco::Node *node) { at(1)->node(node); }
+
+public:
+ bool keep_dims(void) const { return _keep_dims; }
+ void keep_dims(bool keep_dims) { _keep_dims = keep_dims; }
+
+private:
+ bool _keep_dims = false;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMEAN_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h
new file mode 100644
index 000000000..67e897170
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleMul.h
@@ -0,0 +1,45 @@
+/*
+ * 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 __LUCI_IR_CIRCLEMUL_H__
+#define __LUCI_IR_CIRCLEMUL_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief MUL in Circle
+ */
+class CircleMul final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::MUL>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEMUL_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h
new file mode 100644
index 000000000..c65317ad1
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleOutput.h
@@ -0,0 +1,55 @@
+/*
+ * 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 __LUCI_IR_CIRCLEOUTPUT_H__
+#define __LUCI_IR_CIRCLEOUTPUT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+#include <loco/IR/GraphOutputIndex.h>
+
+namespace luci
+{
+
+/**
+ * @brief CircleNode for Output of the Graph
+ * @note This will not be exported as a specific op
+ */
+class CircleOutput final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::CIRCLEOUTPUT>>
+{
+public:
+ CircleOutput() = default;
+
+ void index(const loco::GraphOutputIndex &index);
+ loco::GraphOutputIndex index(void) const;
+
+ bool indexed(void) const { return _index != -1; }
+
+public:
+ loco::Node *from(void) const { return at(0)->node(); }
+ void from(loco::Node *node) { at(0)->node(node); }
+
+private:
+ int64_t _index = -1; // Uninitialized
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEOUTPUT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h
new file mode 100644
index 000000000..8330b585a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePack.h
@@ -0,0 +1,67 @@
+/*
+ * 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 __LUCI_IR_CIRCLEPACK_H__
+#define __LUCI_IR_CIRCLEPACK_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/VariadicArityNode.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief PACK in Circle
+ */
+class CirclePack final : public VariadicArityNode<CircleNodeImpl<CircleOpcode::PACK>>
+{
+public:
+ CirclePack(uint32_t arity) : VariadicArityNode<CircleNodeImpl<CircleOpcode::PACK>>(arity)
+ {
+ // TODO Support when arity is 0
+ assert(arity >= 1);
+ }
+
+public:
+ uint32_t values_count(void) const { return arity(); }
+
+public:
+ Node *values(uint32_t index) const
+ {
+ assert(index < values_count());
+ return at(index)->node();
+ }
+ void values(uint32_t index, Node *node)
+ {
+ assert(index < values_count());
+ at(index)->node(node);
+ }
+
+public:
+ int32_t axis(void) const { return _axis; }
+ void axis(int32_t axis) { _axis = axis; }
+
+private:
+ int32_t _axis{0};
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEPACK_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h
new file mode 100644
index 000000000..31599bda0
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CirclePad.h
@@ -0,0 +1,46 @@
+/*
+ * 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 __LUCI_IR_CIRCLEPAD_H__
+#define __LUCI_IR_CIRCLEPAD_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief PAD in Circle
+ */
+class CirclePad final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::PAD>>
+{
+public:
+ CirclePad() = default;
+
+public:
+ loco::Node *input(void) const { return at(0)->node(); }
+ void input(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *paddings(void) const { return at(1)->node(); }
+ void paddings(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLEPAD_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h
new file mode 100644
index 000000000..afb2c667a
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __LUCI_IR_CIRCLERELU_H__
+#define __LUCI_IR_CIRCLERELU_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RELU in Circle
+ */
+class CircleRelu final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU>>
+{
+public:
+ CircleRelu() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERELU_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h
new file mode 100644
index 000000000..b313a5557
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRelu6.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __LUCI_IR_CIRCLERELU6_H__
+#define __LUCI_IR_CIRCLERELU6_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RELU6 in Circle
+ */
+class CircleRelu6 final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RELU6>>
+{
+public:
+ CircleRelu6() = default;
+
+public:
+ loco::Node *features(void) const { return at(0)->node(); }
+ void features(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERELU6_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h
new file mode 100644
index 000000000..a3a2a3f31
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleReshape.h
@@ -0,0 +1,69 @@
+/*
+ * 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 __LUCI_IR_CIRCLERESHAPE_H__
+#define __LUCI_IR_CIRCLERESHAPE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RESHAPE in Circle
+ */
+class CircleReshape final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::RESHAPE>>
+{
+public:
+ CircleReshape() = default;
+
+public:
+ loco::Node *tensor(void) const { return at(0)->node(); }
+ void tensor(loco::Node *node) { at(0)->node(node); }
+
+ // TODO Make this input optional. That is, loco system does not emit error
+ // with this input being null
+ loco::Node *shape(void) const { return at(1)->node(); }
+ void shape(loco::Node *node) { at(1)->node(node); }
+
+public:
+ class Shape
+ {
+ public:
+ uint32_t rank(void) const { return _shape.size(); }
+ void rank(uint32_t rank) { _shape.resize(rank); }
+
+ int32_t dim(uint32_t n) const { return _shape.at(n); }
+ int32_t &dim(uint32_t n) { return _shape.at(n); }
+
+ private:
+ std::vector<int32_t> _shape;
+ };
+
+ const Shape *newShape(void) const { return &_new_shape; }
+ Shape *newShape(void) { return &_new_shape; }
+
+private:
+ Shape _new_shape;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERESHAPE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h
new file mode 100644
index 000000000..44d22ef22
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleRsqrt.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __LUCI_IR_CIRCLERSQRT_H__
+#define __LUCI_IR_CIRCLERSQRT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief RSQRT in Circle
+ */
+class CircleRsqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::RSQRT>>
+{
+public:
+ CircleRsqrt() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLERSQRT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.h
new file mode 100644
index 000000000..4ea3c4b0e
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSoftmax.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 __LUCI_IR_CIRCLESOFTMAX_H__
+#define __LUCI_IR_CIRCLESOFTMAX_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SOFTMAX in Circle
+ */
+class CircleSoftmax final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SOFTMAX>>
+{
+public:
+ loco::Node *logits(void) const { return at(0)->node(); }
+ void logits(loco::Node *node) { at(0)->node(node); }
+
+public:
+ float beta(void) const { return _beta; }
+ void beta(float beta) { _beta = beta; }
+
+private:
+ float _beta;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESOFTMAX_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h
new file mode 100644
index 000000000..bc1f39d90
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSqrt.h
@@ -0,0 +1,44 @@
+/*
+ * 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 __LUCI_IR_CIRCLESQRT_H__
+#define __LUCI_IR_CIRCLESQRT_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SQRT in Circle
+ */
+class CircleSqrt final : public FixedArityNode<1, CircleNodeImpl<CircleOpcode::SQRT>>
+{
+public:
+ CircleSqrt() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESQRT_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h
new file mode 100644
index 000000000..ff337dfbe
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSquaredDifference.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__
+#define __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SQUARED_DIFFERENCE in Circle
+ */
+class CircleSquaredDifference final
+ : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SQUARED_DIFFERENCE>>
+{
+public:
+ CircleSquaredDifference() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESQUAREDIFFERENCE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h
new file mode 100644
index 000000000..08208f942
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleSub.h
@@ -0,0 +1,48 @@
+/*
+ * 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 __LUCI_IR_CIRCLESUB_H__
+#define __LUCI_IR_CIRCLESUB_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief SUB in Circle
+ */
+class CircleSub final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::SUB>>,
+ public LuciNodeMixin<LuciNodeTrait::FusedActFunc>
+{
+public:
+ CircleSub() = default;
+
+public:
+ loco::Node *x(void) const { return at(0)->node(); }
+ void x(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *y(void) const { return at(1)->node(); }
+ void y(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLESUB_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h
new file mode 100644
index 000000000..198b56afd
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTranspose.h
@@ -0,0 +1,50 @@
+/*
+ * 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 __LUCI_IR_CIRCLETRANSPOSE_H__
+#define __LUCI_IR_CIRCLETRANSPOSE_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TRANSPOSE in Circle
+ */
+class CircleTranspose final : public FixedArityNode<2, CircleNodeImpl<CircleOpcode::TRANSPOSE>>
+{
+public:
+ CircleTranspose() = default;
+
+public:
+ /// @brief Get the input node to transpose
+ loco::Node *a(void) const { return at(0)->node(); }
+
+ /// @brief Set the input node to transpose
+ void a(loco::Node *node) { at(0)->node(node); }
+
+ loco::Node *perm(void) const { return at(1)->node(); }
+ void perm(loco::Node *node) { at(1)->node(node); }
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLETRANSPOSE_H__
diff --git a/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h
new file mode 100644
index 000000000..54a0d010c
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/Nodes/CircleTransposeConv.h
@@ -0,0 +1,64 @@
+/*
+ * 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 __LUCI_IR_CIRCLETRANSPOSECONV_H__
+#define __LUCI_IR_CIRCLETRANSPOSECONV_H__
+
+#include "luci/IR/CircleNodeDecl.h"
+#include "luci/IR/CircleOpcode.h"
+
+#include "luci/IR/AttrPadding.h"
+#include "luci/IR/AttrStride.h"
+#include "luci/IR/AttrFusedActFunc.h"
+#include "luci/IR/LuciNodeMixins.h"
+
+namespace luci
+{
+
+/**
+ * @brief TRANSPOSE_CONV in Circle
+ *
+ * @note Argument node function names are from TensorFlow. So referring 'in' and
+ * 'out' acutally means 'out' and 'in' of the this node.
+ */
+class CircleTransposeConv final
+ : public FixedArityNode<3, CircleNodeImpl<CircleOpcode::TRANSPOSE_CONV>>
+{
+public:
+ loco::Node *inputSizes(void) const { return at(0)->node(); }
+ void inputSizes(Node *node) { at(0)->node(node); }
+
+ loco::Node *filter(void) const { return at(1)->node(); }
+ void filter(Node *node) { at(1)->node(node); }
+
+ loco::Node *outBackprop(void) const { return at(2)->node(); }
+ void outBackprop(Node *node) { at(2)->node(node); }
+
+public:
+ const Padding &padding(void) const { return _padding; }
+ void padding(const Padding &padding) { _padding = padding; }
+
+ const Stride *stride(void) const { return &_stride; }
+ Stride *stride(void) { return &_stride; }
+
+private:
+ Padding _padding;
+ Stride _stride;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_CIRCLETRANSPOSECONV_H__
diff --git a/compiler/luci/lang/include/luci/IR/VariadicArityNode.h b/compiler/luci/lang/include/luci/IR/VariadicArityNode.h
new file mode 100644
index 000000000..a4814ee48
--- /dev/null
+++ b/compiler/luci/lang/include/luci/IR/VariadicArityNode.h
@@ -0,0 +1,77 @@
+/*
+ * 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 __LUCI_IR_VARIADICARITYNODES_H__
+#define __LUCI_IR_VARIADICARITYNODES_H__
+
+#include <loco/IR/Node.h>
+#include <loco/IR/Use.h>
+
+#include <vector>
+#include <memory>
+#include <cassert>
+
+namespace luci
+{
+
+/**
+ * @brief Nodes with the variadic inputs
+ */
+template <typename Base> class VariadicArityNode : public Base
+{
+public:
+ VariadicArityNode(uint32_t arity)
+ {
+ for (uint32_t n = 0; n < arity; ++n)
+ {
+ _args.push_back(std::make_unique<loco::Use>(this));
+ }
+ };
+
+ virtual ~VariadicArityNode() = default;
+
+public:
+ uint32_t arity(void) const final { return _args.size(); }
+
+ loco::Node *arg(uint32_t n) const final
+ {
+ assert(n < _args.size());
+ return _args.at(n)->node();
+ }
+
+ void drop(void) final
+ {
+ for (uint32_t n = 0; n < _args.size(); ++n)
+ {
+ _args.at(n)->node(nullptr);
+ }
+ }
+
+protected:
+ // This API allows inherited classes to access "_args" field.
+ loco::Use *at(uint32_t n) const
+ {
+ assert(n < _args.size());
+ return _args.at(n).get();
+ }
+
+private:
+ std::vector<std::unique_ptr<loco::Use>> _args;
+};
+
+} // namespace luci
+
+#endif // __LUCI_IR_VARIADICARITYNODES_H__
diff --git a/compiler/luci/lang/src/Check.h b/compiler/luci/lang/src/Check.h
new file mode 100644
index 000000000..e05ec904a
--- /dev/null
+++ b/compiler/luci/lang/src/Check.h
@@ -0,0 +1,35 @@
+/*
+ * 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 __CHECK_H__
+#define __CHECK_H__
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define LUCI_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/luci/lang/src/CircleDialect.cpp b/compiler/luci/lang/src/CircleDialect.cpp
new file mode 100644
index 000000000..e1c925de4
--- /dev/null
+++ b/compiler/luci/lang/src/CircleDialect.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "luci/IR/CircleDialect.h"
+#include "luci/IR/Nodes/CircleInput.h"
+#include "luci/IR/Nodes/CircleOutput.h"
+
+#include <loco/IR/Graph.h>
+#include <loco/IR/GraphInputIndex.h>
+#include <loco/IR/GraphOutputIndex.h>
+
+#include <cassert>
+#include <memory>
+
+namespace
+{
+
+struct GiiQueryServiceImpl final : public loco::GraphInputIndexQueryService
+{
+ bool associated(const loco::Node *node) const final
+ {
+ if (auto circleinput = dynamic_cast<const luci::CircleInput *>(node))
+ {
+ return circleinput->indexed();
+ }
+ return false;
+ }
+
+ loco::GraphOutputIndex index(const loco::Node *node) const final
+ {
+ assert(associated(node));
+ auto circleinput = dynamic_cast<const luci::CircleInput *>(node);
+ assert(circleinput != nullptr);
+ return circleinput->index();
+ }
+};
+
+struct GoiQueryServiceImpl final : public loco::GraphOutputIndexQueryService
+{
+ bool associated(const loco::Node *node) const final
+ {
+ if (auto circleoutput = dynamic_cast<const luci::CircleOutput *>(node))
+ {
+ return circleoutput->indexed();
+ }
+ return false;
+ }
+
+ loco::GraphOutputIndex index(const loco::Node *node) const final
+ {
+ assert(associated(node));
+ auto circleoutput = dynamic_cast<const luci::CircleOutput *>(node);
+ assert(circleoutput != nullptr);
+ return circleoutput->index();
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+CircleDialect::CircleDialect()
+{
+ service<loco::GraphInputIndexQueryService>(std::make_unique<GiiQueryServiceImpl>());
+ service<loco::GraphOutputIndexQueryService>(std::make_unique<GoiQueryServiceImpl>());
+}
+
+loco::Dialect *CircleDialect::get(void)
+{
+ static CircleDialect d;
+ return &d;
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/CircleDialect.test.cpp b/compiler/luci/lang/src/CircleDialect.test.cpp
new file mode 100644
index 000000000..78221f199
--- /dev/null
+++ b/compiler/luci/lang/src/CircleDialect.test.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 "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDialectTest, get_P)
+{
+ auto d = luci::CircleDialect::get();
+
+ // get() SHOULD return a valid(non-null) pointer
+ ASSERT_NE(d, nullptr);
+ // The return value SHOULD be stable across multiple invocations
+ ASSERT_EQ(d, luci::CircleDialect::get());
+}
+
+TEST(CircleDialectTest, get_N)
+{
+ // TBD
+}
diff --git a/compiler/luci/lang/src/CircleNode.cpp b/compiler/luci/lang/src/CircleNode.cpp
new file mode 100644
index 000000000..cc273ba91
--- /dev/null
+++ b/compiler/luci/lang/src/CircleNode.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "luci/IR/CircleNode.h"
+#include "luci/IR/CircleDialect.h"
+
+namespace luci
+{
+
+const loco::Dialect *CircleNode::dialect(void) const { return CircleDialect::get(); }
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/CircleNodes.cpp b/compiler/luci/lang/src/CircleNodes.cpp
new file mode 100644
index 000000000..76ff7ec5a
--- /dev/null
+++ b/compiler/luci/lang/src/CircleNodes.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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 "luci/IR/CircleNodes.h"
+
+#include "Check.h"
+
+#include <loco.h>
+
+namespace luci
+{
+
+void set_new_shape(CircleReshape *node, int32_t *base, uint32_t size)
+{
+ // Check node does not have both of new shape infos
+ LUCI_ASSERT(node->shape() == nullptr, "node already has shape input");
+ LUCI_ASSERT(node->newShape()->rank() == 0, "node already has newShape attribute");
+
+ const loco::DataType S32 = loco::DataType::S32;
+
+ // Set 2nd input as CircleConst
+ auto const_shape_node = node->graph()->nodes()->create<CircleConst>();
+ const_shape_node->rank(1);
+ const_shape_node->dim(0) = size;
+ const_shape_node->dtype(S32);
+ const_shape_node->size<S32>(size);
+ for (uint32_t axis = 0; axis < size; ++axis)
+ const_shape_node->at<S32>(axis) = base[axis];
+ node->shape(const_shape_node);
+
+ // Set newShape attribute
+ node->newShape()->rank(size);
+ for (uint32_t axis = 0; axis < size; ++axis)
+ node->newShape()->dim(axis) = base[axis];
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/LuciNodeMixins.cpp b/compiler/luci/lang/src/LuciNodeMixins.cpp
new file mode 100644
index 000000000..660cbe1a5
--- /dev/null
+++ b/compiler/luci/lang/src/LuciNodeMixins.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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.
+ */
+
+// This is to validate LuciNodeMixins.h
+#include "luci/IR/LuciNodeMixins.h"
diff --git a/compiler/luci/lang/src/Module.cpp b/compiler/luci/lang/src/Module.cpp
new file mode 100644
index 000000000..e52d897a5
--- /dev/null
+++ b/compiler/luci/lang/src/Module.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "luci/IR/Module.h"
+
+#include <stdexcept>
+
+namespace luci
+{
+
+void Module::add(std::unique_ptr<loco::Graph> &&g)
+{
+ if (g.get() == nullptr)
+ throw std::invalid_argument("Module: Graph cannot be null");
+
+ _graphs.emplace_back(std::move(g));
+}
+
+loco::Graph *Module::graph(void) const
+{
+ auto &graph = _graphs.at(0);
+ return graph.get();
+}
+
+loco::Graph *Module::graph(size_t idx) const
+{
+ auto &graph = _graphs.at(idx);
+ return graph.get();
+}
+
+std::unique_ptr<Module> make_module(void) { return std::make_unique<Module>(); }
+
+} // namespace loco
diff --git a/compiler/luci/lang/src/Module.test.cpp b/compiler/luci/lang/src/Module.test.cpp
new file mode 100644
index 000000000..f60319944
--- /dev/null
+++ b/compiler/luci/lang/src/Module.test.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "luci/IR/Module.h"
+
+#include <gtest/gtest.h>
+
+TEST(ModuleTest, consturctor)
+{
+ auto gs = luci::make_module();
+
+ GTEST_SUCCEED();
+}
+
+TEST(ModuleTest, add)
+{
+ auto m = luci::make_module();
+ auto g = loco::make_graph();
+ auto g_ptr = g.get();
+
+ m->add(std::move(g));
+
+ ASSERT_EQ(m->graph(), g_ptr);
+ ASSERT_EQ(m->graph(0), g_ptr);
+}
+
+TEST(ModuleTest, add_more)
+{
+ auto m = luci::make_module();
+ auto g1 = loco::make_graph();
+ auto g2 = loco::make_graph();
+ auto g3 = loco::make_graph();
+ auto g1_ptr = g1.get();
+ auto g2_ptr = g2.get();
+ auto g3_ptr = g3.get();
+
+ m->add(std::move(g1));
+ m->add(std::move(g2));
+ m->add(std::move(g3));
+
+ ASSERT_EQ(m->size(), 3);
+ ASSERT_EQ(m->graph(), g1_ptr);
+ ASSERT_EQ(m->graph(0), g1_ptr);
+ ASSERT_EQ(m->graph(1), g2_ptr);
+ ASSERT_EQ(m->graph(2), g3_ptr);
+}
+
+TEST(ModuleTest, add_nullptr_NEG)
+{
+ auto m = luci::make_module();
+
+ EXPECT_THROW(m->add(nullptr), std::invalid_argument);
+}
+
+TEST(ModuleTest, graph_index_overflow_NEG)
+{
+ auto m = luci::make_module();
+
+ EXPECT_ANY_THROW(m->graph(100));
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp b/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp
new file mode 100644
index 000000000..847f1500b
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleAbs.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleAbs.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleAbsTest, constructor)
+{
+ luci::CircleAbs abs_node;
+
+ ASSERT_EQ(abs_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(abs_node.opcode(), luci::CircleOpcode::ABS);
+
+ ASSERT_EQ(abs_node.x(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp b/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp
new file mode 100644
index 000000000..a7701963d
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleAdd.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleAdd.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleAddTest, constructor_P)
+{
+ luci::CircleAdd add_node;
+
+ ASSERT_EQ(add_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(add_node.opcode(), luci::CircleOpcode::ADD);
+
+ ASSERT_EQ(add_node.x(), nullptr);
+ ASSERT_EQ(add_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp b/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp
new file mode 100644
index 000000000..6b2cff11c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleArgMax.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleArgMax.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleArgMaxTest, constructor_P)
+{
+ luci::CircleArgMax add_node;
+
+ ASSERT_EQ(add_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(add_node.opcode(), luci::CircleOpcode::ARG_MAX);
+
+ ASSERT_EQ(add_node.input(), nullptr);
+ ASSERT_EQ(add_node.dimension(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp b/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp
new file mode 100644
index 000000000..e995718a1
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleBatchToSpaceND.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "luci/IR/Nodes/CircleBatchToSpaceND.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleBatchToSpaceNDTest, constructor)
+{
+ luci::CircleBatchToSpaceND bts_node;
+
+ ASSERT_EQ(bts_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(bts_node.opcode(), luci::CircleOpcode::BATCH_TO_SPACE_ND);
+
+ ASSERT_EQ(bts_node.input(), nullptr);
+ ASSERT_EQ(bts_node.block_shape(), nullptr);
+ ASSERT_EQ(bts_node.crops(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp b/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp
new file mode 100644
index 000000000..7167682b2
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleConcatenation.test.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "luci/IR/Nodes/CircleConcatenation.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleConcatenationTest, constructor_P)
+{
+ luci::CircleConcatenation concat_node(3);
+
+ ASSERT_EQ(concat_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(concat_node.opcode(), luci::CircleOpcode::CONCATENATION);
+
+ ASSERT_EQ(concat_node.numValues(), 3);
+ ASSERT_EQ(concat_node.values(0), nullptr);
+ ASSERT_EQ(concat_node.values(1), nullptr);
+ ASSERT_EQ(concat_node.values(2), nullptr);
+ ASSERT_EQ(concat_node.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleConst.cpp b/compiler/luci/lang/src/Nodes/CircleConst.cpp
new file mode 100644
index 000000000..1c46884d8
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleConst.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "luci/IR/Nodes/CircleConst.h"
+
+#include <cassert>
+
+namespace luci
+{
+
+template <loco::DataType DT> uint32_t CircleConst::size(void) const
+{
+ assert(dtype() == DT);
+ assert(_data.size() % sizeof(typename loco::DataTypeImpl<DT>::Type) == 0);
+ return _data.size() / sizeof(typename loco::DataTypeImpl<DT>::Type);
+}
+
+template <loco::DataType DT> void CircleConst::size(uint32_t l)
+{
+ assert(dtype() == DT);
+ _data.resize(l * sizeof(typename loco::DataTypeImpl<DT>::Type));
+}
+
+template <loco::DataType DT>
+const typename loco::DataTypeImpl<DT>::Type &CircleConst::at(uint32_t n) const
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &CircleConst::at(uint32_t n)
+{
+ assert(dtype() == DT);
+ assert(n < size<DT>());
+ return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()) + n);
+}
+
+template <loco::DataType DT>
+const typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar(void) const
+{
+ assert(dtype() == DT);
+ return *(reinterpret_cast<const typename loco::DataTypeImpl<DT>::Type *>(_data.data()));
+}
+
+template <loco::DataType DT> typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar(void)
+{
+ assert(dtype() == DT);
+ return *(reinterpret_cast<typename loco::DataTypeImpl<DT>::Type *>(_data.data()));
+}
+
+#define INSTANTIATE(DT) \
+ template uint32_t CircleConst::size<DT>(void) const; \
+ template void CircleConst::size<DT>(uint32_t); \
+ template const typename loco::DataTypeImpl<DT>::Type &CircleConst::at<DT>(uint32_t) const; \
+ template typename loco::DataTypeImpl<DT>::Type &CircleConst::at<DT>(uint32_t); \
+ template const typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar<DT>(void) const; \
+ template typename loco::DataTypeImpl<DT>::Type &CircleConst::scalar<DT>(void);
+
+INSTANTIATE(loco::DataType::S32);
+INSTANTIATE(loco::DataType::FLOAT32);
+INSTANTIATE(loco::DataType::U8);
+
+#undef INSTANTIATE
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp
new file mode 100644
index 000000000..7931c7eba
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleConv2D.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "luci/IR/Nodes/CircleConv2D.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleConv2Dest, constructor_P)
+{
+ luci::CircleConv2D conv2d_node;
+
+ ASSERT_EQ(conv2d_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(conv2d_node.opcode(), luci::CircleOpcode::CONV_2D);
+
+ ASSERT_EQ(conv2d_node.input(), nullptr);
+ ASSERT_EQ(conv2d_node.filter(), nullptr);
+ ASSERT_EQ(conv2d_node.bias(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleCos.test.cpp b/compiler/luci/lang/src/Nodes/CircleCos.test.cpp
new file mode 100644
index 000000000..34c2cfdf0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleCos.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleCos.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleCosTest, constructor_P)
+{
+ luci::CircleCos cos_node;
+
+ ASSERT_EQ(cos_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(cos_node.opcode(), luci::CircleOpcode::COS);
+
+ ASSERT_EQ(cos_node.x(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp
new file mode 100644
index 000000000..bbc1ea543
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDepthwiseConv2D.test.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "luci/IR/Nodes/CircleDepthwiseConv2D.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDepthwiseConv2DTest, constructor_P)
+{
+ luci::CircleDepthwiseConv2D dw_conv2d_node;
+
+ ASSERT_EQ(dw_conv2d_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(dw_conv2d_node.opcode(), luci::CircleOpcode::DEPTHWISE_CONV_2D);
+
+ ASSERT_EQ(dw_conv2d_node.input(), nullptr);
+ ASSERT_EQ(dw_conv2d_node.filter(), nullptr);
+ ASSERT_EQ(dw_conv2d_node.bias(), nullptr);
+ ASSERT_EQ(dw_conv2d_node.padding(), luci::Padding::UNDEFINED);
+ ASSERT_EQ(dw_conv2d_node.stride()->h(), 1);
+ ASSERT_EQ(dw_conv2d_node.stride()->w(), 1);
+ ASSERT_EQ(dw_conv2d_node.depthMultiplier(), 0);
+ ASSERT_EQ(dw_conv2d_node.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp b/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp
new file mode 100644
index 000000000..e950cc6be
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleDiv.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleDiv.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleDivTest, constructor_P)
+{
+ luci::CircleDiv div_node;
+
+ ASSERT_EQ(div_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(div_node.opcode(), luci::CircleOpcode::DIV);
+
+ ASSERT_EQ(div_node.x(), nullptr);
+ ASSERT_EQ(div_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp b/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp
new file mode 100644
index 000000000..e2757f094
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleEqual.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleEqual.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleEqualTest, constructor_P)
+{
+ luci::CircleEqual or_node;
+
+ ASSERT_EQ(or_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(or_node.opcode(), luci::CircleOpcode::EQUAL);
+
+ ASSERT_EQ(or_node.x(), nullptr);
+ ASSERT_EQ(or_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleExp.test.cpp b/compiler/luci/lang/src/Nodes/CircleExp.test.cpp
new file mode 100644
index 000000000..db10d0b03
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleExp.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleExp.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleExpTest, constructor)
+{
+ luci::CircleExp exp_node;
+
+ ASSERT_EQ(exp_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(exp_node.opcode(), luci::CircleOpcode::EXP);
+
+ ASSERT_EQ(exp_node.x(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp b/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.cpp
new file mode 100644
index 000000000..994dcd239
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleFullyConnected.test.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 "luci/IR/Nodes/CircleFullyConnected.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleFullyConnectedTest, constructor)
+{
+ luci::CircleFullyConnected fc_node;
+
+ ASSERT_EQ(fc_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(fc_node.opcode(), luci::CircleOpcode::FULLY_CONNECTED);
+
+ ASSERT_EQ(fc_node.input(), nullptr);
+ ASSERT_EQ(fc_node.weights(), nullptr);
+ ASSERT_EQ(fc_node.bias(), nullptr);
+ ASSERT_EQ(fc_node.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleGather.test.cpp b/compiler/luci/lang/src/Nodes/CircleGather.test.cpp
new file mode 100644
index 000000000..4eace9a02
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleGather.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "luci/IR/Nodes/CircleGather.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleGatherTest, constructor)
+{
+ luci::CircleGather gather_node;
+
+ ASSERT_EQ(gather_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(gather_node.opcode(), luci::CircleOpcode::GATHER);
+
+ ASSERT_EQ(gather_node.input(), nullptr);
+ ASSERT_EQ(gather_node.positions(), nullptr);
+ ASSERT_EQ(gather_node.axis(), 0);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleInput.cpp b/compiler/luci/lang/src/Nodes/CircleInput.cpp
new file mode 100644
index 000000000..dcf54f3b0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleInput.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "luci/IR/Nodes/CircleInput.h"
+
+#include <cassert>
+#include <limits>
+
+namespace luci
+{
+
+void CircleInput::index(const loco::GraphInputIndex &index)
+{
+ // CircleInput internally stores "GraphInputIndex" as int64_t
+ _index = static_cast<int64_t>(index);
+}
+
+loco::GraphInputIndex CircleInput::index(void) const
+{
+ assert(_index >= std::numeric_limits<loco::GraphInputIndex>::min());
+ assert(_index <= std::numeric_limits<loco::GraphInputIndex>::max());
+ return static_cast<loco::GraphInputIndex>(_index);
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp b/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp
new file mode 100644
index 000000000..b87e81791
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleInstanceNorm.test.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "luci/IR/Nodes/CircleInstanceNorm.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleInstanceNormTest, constructor)
+{
+ luci::CircleInstanceNorm instance_norm;
+
+ ASSERT_EQ(instance_norm.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(instance_norm.opcode(), luci::CircleOpcode::INSTANCE_NORM);
+
+ ASSERT_EQ(instance_norm.input(), nullptr);
+ ASSERT_EQ(instance_norm.gamma(), nullptr);
+ ASSERT_EQ(instance_norm.beta(), nullptr);
+ ASSERT_FLOAT_EQ(instance_norm.epsilon(), 1e-05);
+ ASSERT_EQ(instance_norm.fusedActivationFunction(), luci::FusedActFunc::UNDEFINED);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp
new file mode 100644
index 000000000..360dd4711
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogicalNot.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleLogicalNot.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogicalNotTest, constructor_P)
+{
+ luci::CircleLogicalNot not_node;
+
+ ASSERT_EQ(not_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(not_node.opcode(), luci::CircleOpcode::LOGICAL_NOT);
+
+ ASSERT_EQ(not_node.x(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp b/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp
new file mode 100644
index 000000000..039db4afc
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleLogicalOr.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleLogicalOr.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleLogicalOrTest, constructor_P)
+{
+ luci::CircleLogicalOr or_node;
+
+ ASSERT_EQ(or_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(or_node.opcode(), luci::CircleOpcode::LOGICAL_OR);
+
+ ASSERT_EQ(or_node.x(), nullptr);
+ ASSERT_EQ(or_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp b/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp
new file mode 100644
index 000000000..874ecec0e
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMaxPool2D.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "luci/IR/Nodes/CircleMaxPool2D.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMaxPool2DTest, constructor_P)
+{
+ luci::CircleMaxPool2D maxpool2d_node;
+
+ ASSERT_EQ(maxpool2d_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(maxpool2d_node.opcode(), luci::CircleOpcode::MAX_POOL_2D);
+
+ ASSERT_EQ(maxpool2d_node.value(), nullptr);
+ ASSERT_NE(maxpool2d_node.filter(), nullptr);
+ ASSERT_NE(maxpool2d_node.stride(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp b/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp
new file mode 100644
index 000000000..efe62f11a
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMaximum.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleMaximum.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMaximumTest, constructor_P)
+{
+ luci::CircleMaximum max_node;
+
+ ASSERT_EQ(max_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(max_node.opcode(), luci::CircleOpcode::MAXIMUM);
+
+ ASSERT_EQ(max_node.x(), nullptr);
+ ASSERT_EQ(max_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleMul.test.cpp b/compiler/luci/lang/src/Nodes/CircleMul.test.cpp
new file mode 100644
index 000000000..f9eca42f9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleMul.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleMul.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleMulTest, constructor_P)
+{
+ luci::CircleMul mul_node;
+
+ ASSERT_EQ(mul_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(mul_node.opcode(), luci::CircleOpcode::MUL);
+
+ ASSERT_EQ(mul_node.x(), nullptr);
+ ASSERT_EQ(mul_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleOutput.cpp b/compiler/luci/lang/src/Nodes/CircleOutput.cpp
new file mode 100644
index 000000000..31380456f
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleOutput.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 "luci/IR/Nodes/CircleOutput.h"
+
+#include <cassert>
+#include <limits>
+
+namespace luci
+{
+
+void CircleOutput::index(const loco::GraphOutputIndex &index)
+{
+ // CircleOutput internally stores "GraphOutputIndex" as int64_t
+ _index = static_cast<int64_t>(index);
+}
+
+loco::GraphOutputIndex CircleOutput::index(void) const
+{
+ assert(_index >= std::numeric_limits<loco::GraphOutputIndex>::min());
+ assert(_index <= std::numeric_limits<loco::GraphOutputIndex>::max());
+ return static_cast<loco::GraphOutputIndex>(_index);
+}
+
+} // namespace luci
diff --git a/compiler/luci/lang/src/Nodes/CirclePack.test.cpp b/compiler/luci/lang/src/Nodes/CirclePack.test.cpp
new file mode 100644
index 000000000..5c9a96f7c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePack.test.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 "luci/IR/Nodes/CirclePack.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePackTest, constructor)
+{
+ luci::CirclePack pack_node(3);
+
+ ASSERT_EQ(pack_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(pack_node.opcode(), luci::CircleOpcode::PACK);
+
+ ASSERT_EQ(pack_node.axis(), 0);
+ ASSERT_EQ(pack_node.values_count(), 3);
+ ASSERT_EQ(pack_node.values(0), nullptr);
+ ASSERT_EQ(pack_node.values(1), nullptr);
+ ASSERT_EQ(pack_node.values(2), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CirclePad.test.cpp b/compiler/luci/lang/src/Nodes/CirclePad.test.cpp
new file mode 100644
index 000000000..3a23fa0f0
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CirclePad.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CirclePad.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CirclePadTest, constructor_P)
+{
+ luci::CirclePad pad_node;
+
+ ASSERT_EQ(pad_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(pad_node.opcode(), luci::CircleOpcode::PAD);
+
+ ASSERT_EQ(pad_node.input(), nullptr);
+ ASSERT_EQ(pad_node.paddings(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp b/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp
new file mode 100644
index 000000000..19ea88aa6
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRelu.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleRelu.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReluTest, constructor_P)
+{
+ luci::CircleRelu relu_node;
+
+ ASSERT_EQ(relu_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(relu_node.opcode(), luci::CircleOpcode::RELU);
+
+ ASSERT_EQ(relu_node.features(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp b/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp
new file mode 100644
index 000000000..74bf2e86a
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRelu6.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleRelu6.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRelu6Test, constructor_P)
+{
+ luci::CircleRelu6 relu6_node;
+
+ ASSERT_EQ(relu6_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(relu6_node.opcode(), luci::CircleOpcode::RELU6);
+
+ ASSERT_EQ(relu6_node.features(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp b/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp
new file mode 100644
index 000000000..7bc2d32a4
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleReshape.test.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "luci/IR/Nodes/CircleReshape.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleReshapeTest, constructor_P)
+{
+ luci::CircleReshape reshape;
+
+ ASSERT_EQ(reshape.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(reshape.opcode(), luci::CircleOpcode::RESHAPE);
+
+ ASSERT_EQ(reshape.tensor(), nullptr);
+ ASSERT_EQ(reshape.shape(), nullptr);
+ ASSERT_EQ(reshape.newShape()->rank(), 0);
+}
+
+TEST(CircleReshapeTest, alloc_new_shape_P)
+{
+ luci::CircleReshape reshape;
+
+ reshape.newShape()->rank(2);
+ ASSERT_EQ(reshape.newShape()->rank(), 2);
+
+ reshape.newShape()->dim(0) = 0;
+ reshape.newShape()->dim(1) = 1;
+
+ auto &const_reshape = const_cast<const luci::CircleReshape &>(reshape);
+ ASSERT_EQ(const_reshape.newShape()->dim(0), 0);
+ ASSERT_EQ(const_reshape.newShape()->dim(1), 1);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp b/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp
new file mode 100644
index 000000000..51f6bab36
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleRsqrt.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleRsqrt.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleRsqrtTest, constructor)
+{
+ luci::CircleRsqrt rsqrt_node;
+
+ ASSERT_EQ(rsqrt_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(rsqrt_node.opcode(), luci::CircleOpcode::RSQRT);
+
+ ASSERT_EQ(rsqrt_node.x(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp b/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp
new file mode 100644
index 000000000..7e994490c
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSoftmax.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleSoftmax.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSoftmaxTest, constructor_P)
+{
+ luci::CircleSoftmax softmax_node;
+
+ ASSERT_EQ(softmax_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(softmax_node.opcode(), luci::CircleOpcode::SOFTMAX);
+
+ ASSERT_EQ(softmax_node.logits(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp b/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp
new file mode 100644
index 000000000..6cfb3bc94
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSqrt.test.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "luci/IR/Nodes/CircleSqrt.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSqrtTest, constructor_P)
+{
+ luci::CircleSqrt sqrt_node;
+
+ ASSERT_EQ(sqrt_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(sqrt_node.opcode(), luci::CircleOpcode::SQRT);
+
+ ASSERT_EQ(sqrt_node.x(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp b/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp
new file mode 100644
index 000000000..71df189b9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSquaredDifference.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleSquaredDifference.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSquaredDifferenceTest, constructor_P)
+{
+ luci::CircleSquaredDifference sd_node;
+
+ ASSERT_EQ(sd_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(sd_node.opcode(), luci::CircleOpcode::SQUARED_DIFFERENCE);
+
+ ASSERT_EQ(sd_node.x(), nullptr);
+ ASSERT_EQ(sd_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleSub.test.cpp b/compiler/luci/lang/src/Nodes/CircleSub.test.cpp
new file mode 100644
index 000000000..ebb29446a
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleSub.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleSub.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleSubTest, constructor_P)
+{
+ luci::CircleSub sub_node;
+
+ ASSERT_EQ(sub_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(sub_node.opcode(), luci::CircleOpcode::SUB);
+
+ ASSERT_EQ(sub_node.x(), nullptr);
+ ASSERT_EQ(sub_node.y(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp b/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp
new file mode 100644
index 000000000..7233869e6
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTranspose.test.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "luci/IR/Nodes/CircleTranspose.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTransposeTest, constructor_P)
+{
+ luci::CircleTranspose tr_node;
+
+ ASSERT_EQ(tr_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(tr_node.opcode(), luci::CircleOpcode::TRANSPOSE);
+
+ ASSERT_EQ(tr_node.a(), nullptr);
+ ASSERT_EQ(tr_node.perm(), nullptr);
+}
diff --git a/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp b/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp
new file mode 100644
index 000000000..9615082d9
--- /dev/null
+++ b/compiler/luci/lang/src/Nodes/CircleTransposeConv.test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "luci/IR/Nodes/CircleTransposeConv.h"
+
+#include "luci/IR/CircleDialect.h"
+
+#include <gtest/gtest.h>
+
+TEST(CircleTransposeConvTest, constructor_P)
+{
+ luci::CircleTransposeConv trc_node;
+
+ ASSERT_EQ(trc_node.dialect(), luci::CircleDialect::get());
+ ASSERT_EQ(trc_node.opcode(), luci::CircleOpcode::TRANSPOSE_CONV);
+
+ ASSERT_EQ(trc_node.inputSizes(), nullptr);
+ ASSERT_EQ(trc_node.filter(), nullptr);
+ ASSERT_EQ(trc_node.outBackprop(), nullptr);
+}
diff --git a/compiler/luci/log/CMakeLists.txt b/compiler/luci/log/CMakeLists.txt
new file mode 100644
index 000000000..af2e7a768
--- /dev/null
+++ b/compiler/luci/log/CMakeLists.txt
@@ -0,0 +1,9 @@
+# TODO Find how to test logging framework
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(luci_log SHARED ${SOURCES})
+target_include_directories(luci_log PUBLIC include)
+target_link_libraries(luci_log PUBLIC hermes)
+target_link_libraries(luci_log PRIVATE hermes_std)
+target_link_libraries(luci_log PRIVATE nncc_common)
+install(TARGETS luci_log DESTINATION lib)
diff --git a/compiler/luci/log/README.md b/compiler/luci/log/README.md
new file mode 100644
index 000000000..512bc96d2
--- /dev/null
+++ b/compiler/luci/log/README.md
@@ -0,0 +1,3 @@
+# luci-log
+
+_luci-log_ is a logging framework for _luci_ compiler framework.
diff --git a/compiler/luci/log/include/luci/Log.h b/compiler/luci/log/include/luci/Log.h
new file mode 100644
index 000000000..51299a082
--- /dev/null
+++ b/compiler/luci/log/include/luci/Log.h
@@ -0,0 +1,75 @@
+/*
+ * 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 __LUCI_LOG_H__
+#define __LUCI_LOG_H__
+
+#include <hermes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Logger Implementation
+ */
+class Logger final : public hermes::Source
+{
+public:
+ Logger(hermes::Context *ctx);
+ ~Logger();
+};
+
+/**
+ * @brief Logger Configuration
+ *
+ * Users are able to turn logging on/off via MOCO_LOG environment variable.
+ */
+class LoggerConfig final : public hermes::Config
+{
+public:
+ LoggerConfig();
+
+public:
+ void configure(const hermes::Source *, hermes::Source::Setting &) const final;
+ void configure(const Logger *, hermes::Source::Setting &) const;
+
+private:
+ bool _enabled;
+};
+
+} // namespace luci
+
+#include "luci/LoggingContext.h"
+
+/**
+ * HOW TO USE:
+ *
+ * LOGGER(l);
+ *
+ * INFO(l) << "Hello, World" << std::endl;
+ *
+ */
+#define LOGGER(name) ::luci::Logger name{::luci::LoggingContext::get()};
+
+// TODO Support FATAL, ERROR, WARN, and VERBOSE
+#define INFO(name) HERMES_INFO(name)
+
+// WARNING!
+//
+// THE CURRENT IMPLEMENTATION IS NOT THREAD SAFE.
+//
+
+#endif // __LUCI_LOG_H__
diff --git a/compiler/luci/log/include/luci/LoggingContext.h b/compiler/luci/log/include/luci/LoggingContext.h
new file mode 100644
index 000000000..f5091099f
--- /dev/null
+++ b/compiler/luci/log/include/luci/LoggingContext.h
@@ -0,0 +1,35 @@
+/*
+ * 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 __LUCI_LOGGING_CONTEXT_H__
+#define __LUCI_LOGGING_CONTEXT_H__
+
+#include <hermes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Global logging context
+ */
+struct LoggingContext
+{
+ static hermes::Context *get(void);
+};
+
+} // namespace luci
+
+#endif // __LUCI_LOGGING_CONTEXT_H__
diff --git a/compiler/luci/log/src/Log.cpp b/compiler/luci/log/src/Log.cpp
new file mode 100644
index 000000000..7e1634009
--- /dev/null
+++ b/compiler/luci/log/src/Log.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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 "luci/Log.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <iostream>
+
+// TODO Extract these lexical conversion routines as a library
+namespace
+{
+
+/**
+ * @brief Convert C-string as a value of type T
+ *
+ * safecast(s, v) returns v if s is nullptr.
+ */
+template <typename T> T safecast(const char *, const T &);
+
+template <> bool safecast<bool>(const char *s, const bool &value)
+{
+ return (s == nullptr) ? value : (std::stoi(s) != 0);
+}
+
+} // namespace
+
+//
+// Logger
+//
+namespace luci
+{
+
+Logger::Logger(hermes::Context *ctx) { activate(ctx->sources(), ctx->bus()); }
+Logger::~Logger() { deactivate(); }
+
+} // namespace luci
+
+//
+// LoggerConfig
+//
+namespace luci
+{
+
+LoggerConfig::LoggerConfig()
+{
+ // Turn on logging if LUCI_LOG is set as non-zero value
+ _enabled = safecast<bool>(std::getenv("LUCI_LOG"), false);
+}
+
+void LoggerConfig::configure(const hermes::Source *source, hermes::Source::Setting &setting) const
+{
+ // Let's ignore hermes::Sources if that is not a moco logger
+ if (auto logger = dynamic_cast<const Logger *>(source))
+ {
+ configure(logger, setting);
+ }
+}
+
+void LoggerConfig::configure(const Logger *, hermes::Source::Setting &setting) const
+{
+ if (_enabled)
+ {
+ // Enable all catagories
+ setting.accept_all();
+ }
+ else
+ {
+ // Disable all catagories
+ setting.reject_all();
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/log/src/LoggingContext.cpp b/compiler/luci/log/src/LoggingContext.cpp
new file mode 100644
index 000000000..8d7997869
--- /dev/null
+++ b/compiler/luci/log/src/LoggingContext.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "luci/LoggingContext.h"
+#include "luci/Log.h"
+
+#include <hermes/ConsoleReporter.h>
+
+#include <memory>
+
+namespace luci
+{
+
+hermes::Context *LoggingContext::get(void)
+{
+ static hermes::Context *ctx = nullptr;
+
+ if (ctx == nullptr)
+ {
+ ctx = new hermes::Context;
+ ctx->sinks()->append(std::make_unique<hermes::ConsoleReporter>());
+ ctx->config(std::make_unique<LoggerConfig>());
+ }
+
+ return ctx;
+}
+
+} // namespace luci
diff --git a/compiler/luci/logex/CMakeLists.txt b/compiler/luci/logex/CMakeLists.txt
new file mode 100644
index 000000000..fa2ea123c
--- /dev/null
+++ b/compiler/luci/logex/CMakeLists.txt
@@ -0,0 +1,13 @@
+# TODO Find how to test logging-ex utility
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+
+add_library(luci_logex SHARED ${SOURCES})
+target_include_directories(luci_logex PUBLIC include)
+target_link_libraries(luci_logex PUBLIC loco)
+target_link_libraries(luci_logex PUBLIC locop)
+target_link_libraries(luci_logex PRIVATE luci_log)
+target_link_libraries(luci_logex PRIVATE luci_lang)
+target_link_libraries(luci_logex PRIVATE hermes_std)
+target_link_libraries(luci_logex PRIVATE nncc_common)
+target_link_libraries(luci_logex PRIVATE pepper_str)
+install(TARGETS luci_logex DESTINATION lib)
diff --git a/compiler/luci/logex/README.md b/compiler/luci/logex/README.md
new file mode 100644
index 000000000..03b6baf35
--- /dev/null
+++ b/compiler/luci/logex/README.md
@@ -0,0 +1,3 @@
+# luci-logex
+
+_luci-logex_ is a extended logging utility for _luci_ compiler framework.
diff --git a/compiler/luci/logex/include/luci/FormattedGraph.h b/compiler/luci/logex/include/luci/FormattedGraph.h
new file mode 100644
index 000000000..da4af3bfa
--- /dev/null
+++ b/compiler/luci/logex/include/luci/FormattedGraph.h
@@ -0,0 +1,56 @@
+/*
+ * 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 __LUCI_FORMATTED_GRAPH_H__
+#define __LUCI_FORMATTED_GRAPH_H__
+
+#include <locop/FormattedGraph.h>
+
+#include <memory>
+
+namespace luci
+{
+
+class NodeSummaryBuilder final : public locop::NodeSummaryBuilder
+{
+public:
+ NodeSummaryBuilder(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 NodeSummaryBuilderFactory final : public locop::NodeSummaryBuilderFactory
+{
+public:
+ NodeSummaryBuilderFactory() = default;
+
+public:
+ std::unique_ptr<locop::NodeSummaryBuilder> create(const locop::SymbolTable *tlb) const final
+ {
+ return std::make_unique<NodeSummaryBuilder>(tlb);
+ }
+};
+
+} // namespace luci
+
+#endif // __LUCI_FORMATTED_GRAPH_H__
diff --git a/compiler/luci/logex/include/luci/LogHelper.h b/compiler/luci/logex/include/luci/LogHelper.h
new file mode 100644
index 000000000..37cdd735b
--- /dev/null
+++ b/compiler/luci/logex/include/luci/LogHelper.h
@@ -0,0 +1,36 @@
+/*
+ * 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 __LUCI_LOG_HELPER_H__
+#define __LUCI_LOG_HELPER_H__
+
+#include <locop/FormattedGraph.h>
+#include <loco.h>
+
+#include <memory>
+
+namespace luci
+{
+
+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 luci
+
+#endif // __LUCI_LOG_HELPER_H__
diff --git a/compiler/luci/logex/src/FormattedGraph.cpp b/compiler/luci/logex/src/FormattedGraph.cpp
new file mode 100644
index 000000000..894ebc151
--- /dev/null
+++ b/compiler/luci/logex/src/FormattedGraph.cpp
@@ -0,0 +1,606 @@
+/*
+ * 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 "luci/FormattedGraph.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodes.h>
+
+#include <pepper/str.h>
+
+#include <cassert>
+#include <sstream>
+#include <vector>
+
+/**
+ * @brief dump std::vector<int64_t> values to stream
+ */
+std::ostream &operator<<(std::ostream &os, const std::vector<int64_t> &vi64)
+{
+ for (auto vi : vi64)
+ {
+ os << vi << " ";
+ }
+ return os;
+}
+
+// For TF lite
+namespace
+{
+
+const char *to_str(loco::DataType type)
+{
+ switch (type)
+ {
+ case loco::DataType::U8:
+ return "UINT8";
+ case loco::DataType::U16:
+ return "UINT16";
+ case loco::DataType::U32:
+ return "UINT32";
+ case loco::DataType::U64:
+ return "UINT64";
+
+ case loco::DataType::S8:
+ return "INT8";
+ case loco::DataType::S16:
+ return "INT16";
+ case loco::DataType::S32:
+ return "INT32";
+ case loco::DataType::S64:
+ return "INT64";
+
+ case loco::DataType::FLOAT16:
+ return "FLOAT16";
+ case loco::DataType::FLOAT32:
+ return "FLOAT32";
+ case loco::DataType::FLOAT64:
+ return "FLOAT64";
+
+ case loco::DataType::BOOL:
+ return "BOOL";
+
+ default:
+ return "Error";
+ }
+}
+
+const char *to_str(luci::FusedActFunc fused)
+{
+ switch (fused)
+ {
+ case luci::FusedActFunc::NONE:
+ return "NONE";
+ case luci::FusedActFunc::RELU:
+ return "RELU";
+ case luci::FusedActFunc::RELU_N1_TO_1:
+ return "RELU_N1_TO_1";
+ case luci::FusedActFunc::RELU6:
+ return "RELU6";
+ default:
+ return "Error";
+ }
+}
+
+const char *to_str(luci::Padding padding)
+{
+ switch (padding)
+ {
+ case luci::Padding::SAME:
+ return "SAME";
+ case luci::Padding::VALID:
+ return "VALID";
+ default:
+ return "Error";
+ }
+}
+
+std::string to_str(const luci::Stride *stride)
+{
+ return pepper::str(stride->h(), ",", stride->w());
+}
+
+std::string to_str(const luci::Filter *filter)
+{
+ return pepper::str(filter->h(), ",", filter->w());
+}
+
+std::string circle_opname(uint32_t opnum)
+{
+ static const std::string prefix{"circle."};
+
+ switch (static_cast<luci::CircleOpcode>(opnum))
+ {
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ case luci::CircleOpcode::OPCODE: \
+ return prefix + #OPCODE;
+#include <luci/IR/CircleNodes.lst>
+#undef CIRCLE_NODE
+ default:
+ break;
+ };
+
+ return prefix + "Invalid";
+}
+
+// CircleNodeSummaryBuilder with default implementation
+class CircleNodeSummaryBuilderBase : public locop::NodeSummaryBuilder
+{
+public:
+ CircleNodeSummaryBuilderBase(const locop::SymbolTable *tbl) : _tbl{tbl}
+ {
+ // DO NOTHING
+ }
+
+public:
+ bool build(const loco::Node *, locop::NodeSummary &s) const final;
+
+protected:
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ virtual bool summary(const CLASS *, locop::NodeSummary &s) const \
+ { \
+ s.comments().append("Emitted by Default CircleNodeSummaryBuilder"); \
+ s.state(locop::NodeSummary::State::PartiallyKnown); \
+ return true; \
+ }
+#include <luci/IR/CircleNodes.lst>
+#undef CIRCLE_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 CircleNodeSummaryBuilder final : public CircleNodeSummaryBuilderBase
+{
+public:
+ CircleNodeSummaryBuilder(const locop::SymbolTable *tbl) : CircleNodeSummaryBuilderBase(tbl)
+ {
+ // DO NOTHING
+ }
+
+private:
+#define IMPLEMENT(CLASS) bool summary(const CLASS *, locop::NodeSummary &) const final;
+ IMPLEMENT(luci::CircleAbs)
+ IMPLEMENT(luci::CircleAdd)
+ IMPLEMENT(luci::CircleArgMax)
+ IMPLEMENT(luci::CircleAveragePool2D)
+ IMPLEMENT(luci::CircleBatchToSpaceND)
+ IMPLEMENT(luci::CircleConcatenation)
+ IMPLEMENT(luci::CircleConst)
+ IMPLEMENT(luci::CircleConv2D)
+ IMPLEMENT(luci::CircleCos)
+ IMPLEMENT(luci::CircleDepthwiseConv2D)
+ IMPLEMENT(luci::CircleDiv)
+ IMPLEMENT(luci::CircleExp)
+ IMPLEMENT(luci::CircleFullyConnected)
+ IMPLEMENT(luci::CircleLogicalNot)
+ IMPLEMENT(luci::CircleLogicalOr)
+ IMPLEMENT(luci::CircleMaximum)
+ IMPLEMENT(luci::CircleMaxPool2D)
+ IMPLEMENT(luci::CircleMean)
+ IMPLEMENT(luci::CircleMul)
+ IMPLEMENT(luci::CirclePack)
+ IMPLEMENT(luci::CirclePad)
+ IMPLEMENT(luci::CircleRelu)
+ IMPLEMENT(luci::CircleRelu6)
+ IMPLEMENT(luci::CircleReshape)
+ IMPLEMENT(luci::CircleRsqrt)
+ IMPLEMENT(luci::CircleSoftmax)
+ IMPLEMENT(luci::CircleSqrt)
+ IMPLEMENT(luci::CircleSquaredDifference)
+ IMPLEMENT(luci::CircleSub)
+ IMPLEMENT(luci::CircleTranspose)
+ IMPLEMENT(luci::CircleTransposeConv)
+ // Circle Only
+ IMPLEMENT(luci::CircleInstanceNorm)
+ // Virtual nodes
+ IMPLEMENT(luci::CircleInput)
+ IMPLEMENT(luci::CircleOutput)
+#undef IMPLEMENT
+};
+
+bool CircleNodeSummaryBuilderBase::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (node->dialect() != luci::CircleDialect::get())
+ return false;
+
+#define CIRCLE_NODE(OPCODE, CLASS) \
+ if (dynamic_cast<const CLASS *>(node)) \
+ { \
+ s.opname(circle_opname(node->opnum())); \
+ return summary(dynamic_cast<const CLASS *>(node), s); \
+ }
+#include <luci/IR/CircleNodes.lst>
+#undef CIRCLE_NODE
+
+ return false;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAbs *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAdd *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.args().append("fused_activation_function", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleArgMax *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("dimension", tbl()->lookup(node->dimension()));
+ s.args().append("output_type", to_str(node->output_type()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleAveragePool2D *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("filter(h,w)", to_str(node->filter()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleBatchToSpaceND *node,
+ locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("block_shape", tbl()->lookup(node->block_shape()));
+ s.args().append("crops", tbl()->lookup(node->crops()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleConcatenation *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ for (uint32_t i = 0; i < node->numValues(); ++i)
+ s.args().append("values", tbl()->lookup(node->values(i)));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleConst *, locop::NodeSummary &s) const
+{
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleConv2D *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+ assert(node->padding() != luci::Padding::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("bias", tbl()->lookup(node->bias()));
+
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleCos *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDepthwiseConv2D *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+ assert(node->padding() != luci::Padding::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("bias", tbl()->lookup(node->bias()));
+
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("depthMultiplier", std::to_string(node->depthMultiplier()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleDiv *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 CircleNodeSummaryBuilder::summary(const luci::CircleExp *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleFullyConnected *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("weights", tbl()->lookup(node->weights()));
+ s.args().append("bias", tbl()->lookup(node->bias()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalNot *node,
+ locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleLogicalOr *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 CircleNodeSummaryBuilder::summary(const luci::CircleMaximum *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 CircleNodeSummaryBuilder::summary(const luci::CircleMaxPool2D *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("value", tbl()->lookup(node->value()));
+ s.args().append("filter(h,w)", to_str(node->filter()));
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+ s.args().append("fused", to_str(node->fusedActivationFunction()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMean *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", node->keep_dims() ? "true" : "false");
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleMul *node, locop::NodeSummary &s) const
+{
+ assert(node->fusedActivationFunction() != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.args().append("y", tbl()->lookup(node->y()));
+ s.args().append("fused_activation_function", to_str(node->fusedActivationFunction()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePack *node, locop::NodeSummary &s) const
+{
+ for (uint32_t i = 0; i < node->values_count(); ++i)
+ s.args().append("values", tbl()->lookup(node->values(i)));
+ s.args().append("values_count", pepper::str(node->values_count()));
+ s.args().append("axis", pepper::str(node->axis()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CirclePad *node, locop::NodeSummary &s) const
+{
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("paddings", tbl()->lookup(node->paddings()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRelu *node, locop::NodeSummary &s) const
+{
+ s.args().append("features", tbl()->lookup(node->features()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRelu6 *node, locop::NodeSummary &s) const
+{
+ s.args().append("features", tbl()->lookup(node->features()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleReshape *node, locop::NodeSummary &s) const
+{
+ s.args().append("tensor", tbl()->lookup(node->tensor()));
+ s.args().append("shape", tbl()->lookup(node->shape()));
+ // TODO Show newShape info
+ s.state(locop::NodeSummary::State::PartiallyKnown);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleRsqrt *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSoftmax *node, locop::NodeSummary &s) const
+{
+ s.args().append("logits", tbl()->lookup(node->logits()));
+ s.args().append("beta", pepper::str(node->beta()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSqrt *node, locop::NodeSummary &s) const
+{
+ s.args().append("x", tbl()->lookup(node->x()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleSquaredDifference *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 CircleNodeSummaryBuilder::summary(const luci::CircleSub *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;
+}
+
+// TODO TFLTanh
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTranspose *node,
+ locop::NodeSummary &s) const
+{
+ s.args().append("a", tbl()->lookup(node->a()));
+ s.args().append("perm", tbl()->lookup(node->perm()));
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleTransposeConv *node,
+ locop::NodeSummary &s) const
+{
+ assert(node->padding() != luci::Padding::UNDEFINED);
+
+ s.args().append("inputSizes", tbl()->lookup(node->inputSizes()));
+ s.args().append("filter", tbl()->lookup(node->filter()));
+ s.args().append("outBackprop", tbl()->lookup(node->outBackprop()));
+
+ s.args().append("stride(h,w)", to_str(node->stride()));
+ s.args().append("padding", to_str(node->padding()));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleInput *, locop::NodeSummary &s) const
+{
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleOutput *node, locop::NodeSummary &s) const
+{
+ s.args().append("from", tbl()->lookup(node->from()));
+
+ s.state(locop::NodeSummary::State::Complete);
+ return true;
+}
+
+bool CircleNodeSummaryBuilder::summary(const luci::CircleInstanceNorm *node,
+ locop::NodeSummary &s) const
+{
+ auto fused = node->fusedActivationFunction();
+ assert(fused != luci::FusedActFunc::UNDEFINED);
+
+ s.args().append("input", tbl()->lookup(node->input()));
+ s.args().append("gamma", tbl()->lookup(node->gamma()));
+ s.args().append("beta", tbl()->lookup(node->beta()));
+ s.args().append("epsilon", pepper::str(node->epsilon()));
+ s.args().append("fused_activation_function", to_str(fused));
+
+ s.state(locop::NodeSummary::State::Complete);
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool NodeSummaryBuilder::build(const loco::Node *node, locop::NodeSummary &s) const
+{
+ if (locop::CanonicalNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ if (CircleNodeSummaryBuilder(_tbl).build(node, s))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace exo
diff --git a/compiler/luci/logex/src/LogHelper.cpp b/compiler/luci/logex/src/LogHelper.cpp
new file mode 100644
index 000000000..caf659906
--- /dev/null
+++ b/compiler/luci/logex/src/LogHelper.cpp
@@ -0,0 +1,29 @@
+/*
+ * 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 "luci/LogHelper.h"
+#include "luci/FormattedGraph.h"
+
+namespace luci
+{
+
+FormattedGraph fmt(loco::Graph *g)
+{
+ auto node_summary_builder = std::make_unique<NodeSummaryBuilderFactory>();
+ return std::move(locop::fmt<locop::LinearV1>(g).with(std::move(node_summary_builder)));
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/CMakeLists.txt b/compiler/luci/pass/CMakeLists.txt
new file mode 100644
index 000000000..93130ce60
--- /dev/null
+++ b/compiler/luci/pass/CMakeLists.txt
@@ -0,0 +1,29 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+#file(GLOB_RECURSE TESTS "src/*.test.cpp")
+#list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_pass SHARED ${SOURCES})
+target_include_directories(luci_pass PRIVATE src)
+target_include_directories(luci_pass PUBLIC include)
+target_link_libraries(luci_pass PUBLIC loco)
+target_link_libraries(luci_pass PUBLIC logo_core)
+target_link_libraries(luci_pass PRIVATE logo)
+target_link_libraries(luci_pass PRIVATE luci_lang)
+target_link_libraries(luci_pass PRIVATE luci_log)
+target_link_libraries(luci_pass PRIVATE luci_service)
+target_link_libraries(luci_pass PRIVATE luci_logex)
+target_link_libraries(luci_pass PRIVATE nncc_common)
+target_link_libraries(luci_pass PRIVATE oops)
+install(TARGETS luci_pass DESTINATION lib)
+
+# TODO enable for tests
+#if(NOT ENABLE_TEST)
+# return()
+#endif(NOT ENABLE_TEST)
+#
+#nnas_find_package(GTest REQUIRED)
+#
+#GTest_AddTest(luci_pass_test ${TESTS})
+#target_include_directories(luci_pass_test PRIVATE src)
+#target_link_libraries(luci_pass_test luci_pass)
+#target_link_libraries(luci_pass_test oops)
diff --git a/compiler/luci/pass/README.md b/compiler/luci/pass/README.md
new file mode 100644
index 000000000..9b6cdebd3
--- /dev/null
+++ b/compiler/luci/pass/README.md
@@ -0,0 +1,3 @@
+# luci-pass
+
+_luci-pass_ provides Circle Dialect transformation passes
diff --git a/compiler/luci/pass/include/luci/CircleOptimizer.h b/compiler/luci/pass/include/luci/CircleOptimizer.h
new file mode 100644
index 000000000..a969cca85
--- /dev/null
+++ b/compiler/luci/pass/include/luci/CircleOptimizer.h
@@ -0,0 +1,55 @@
+/*
+ * 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 __LUCI_CIRCLE_OPTIMIZER_H__
+#define __LUCI_CIRCLE_OPTIMIZER_H__
+
+#include <loco.h>
+
+#include <string>
+#include <vector>
+
+namespace luci
+{
+
+class CircleOptimizer final
+{
+public:
+ struct Options
+ {
+ enum Algorithm
+ {
+ FuseInstanceNorm,
+ };
+
+ virtual void enable(Algorithm) = 0;
+ virtual bool query(Algorithm) = 0;
+ };
+
+public:
+ // TODO maybe caller can provide Options as ctor parameters
+ Options *options(void);
+
+public:
+ void optimize(loco::Graph *) const;
+
+private:
+ std::unique_ptr<Options> _options;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_OPTIMIZER_H__
diff --git a/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h b/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h
new file mode 100644
index 000000000..800a5f789
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/FuseInstanceNormPass.h
@@ -0,0 +1,40 @@
+/*
+ * 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 __LUCI_FUSE_INSTANCE_NORM_PASS_H__
+#define __LUCI_FUSE_INSTANCE_NORM_PASS_H__
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Class to fuse certain pattern of subgraph into CircleInstanceNorm
+ * with auxiliary nodes
+ *
+ * For detailed subgraph pattern to be fused, please check its implementation.
+ */
+struct FuseInstanceNormPass final : public logo::Pass
+{
+ const char *name(void) const final { return "luci::FuseInstanceNormPass"; }
+
+ bool run(loco::Graph *g) final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_FUSE_INSTANCE_NORM_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h b/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h
new file mode 100644
index 000000000..86bb2ab42
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/ShapeInferencePass.h
@@ -0,0 +1,41 @@
+/*
+ * 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 __LUCI_SHAPE_INFERENCE_PASS_H__
+#define __LUCI_SHAPE_INFERENCE_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to infer shape of nodes
+ */
+class ShapeInferencePass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "luci::ShapeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace luci
+
+#endif //__LUCI_SHAPE_INFERENCE_PASS_H__
diff --git a/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h b/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h
new file mode 100644
index 000000000..c607ac63f
--- /dev/null
+++ b/compiler/luci/pass/include/luci/Pass/TypeInferencePass.h
@@ -0,0 +1,42 @@
+
+/*
+ * 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 __LUCI_TYPE_INFERENCE_PASS_H__
+#define __LUCI_TYPE_INFERENCE_PASS_H__
+
+#include <loco.h>
+
+#include <logo/Pass.h>
+
+namespace luci
+{
+
+/**
+ * @brief Pass to infer type of nodes
+ */
+class TypeInferencePass : public logo::Pass
+{
+public:
+ virtual const char *name(void) const { return "luci::TypeInferencePass"; }
+
+public:
+ bool run(loco::Graph *graph);
+};
+
+} // namespace luci
+
+#endif //__LUCI_TYPE_INFERENCE_PASS_H__
diff --git a/compiler/luci/pass/src/CircleOptimizer.cpp b/compiler/luci/pass/src/CircleOptimizer.cpp
new file mode 100644
index 000000000..dcb05a0b5
--- /dev/null
+++ b/compiler/luci/pass/src/CircleOptimizer.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "luci/CircleOptimizer.h"
+
+#include "luci/Pass/FuseInstanceNormPass.h"
+// TODO add more passes
+
+#include "luci/Pass/ShapeInferencePass.h"
+#include "luci/Pass/TypeInferencePass.h"
+
+// logo passes
+#include <logo/RemoveDeadNodePass.h>
+
+#include "ProgressReporter.h"
+
+#include <logo/Phase.h>
+
+#include <memory>
+
+namespace
+{
+
+using namespace luci;
+
+class OptimizeOptionsImpl : public luci::CircleOptimizer::Options
+{
+public:
+ void enable(Algorithm) final;
+ bool query(Algorithm) final;
+
+private:
+ std::vector<Algorithm> _algorithms;
+};
+
+void OptimizeOptionsImpl::enable(Algorithm algo) { _algorithms.push_back(algo); }
+
+bool OptimizeOptionsImpl::query(Algorithm algo)
+{
+ std::vector<Algorithm>::iterator it = std::find(_algorithms.begin(), _algorithms.end(), algo);
+ if (it == _algorithms.end())
+ return false;
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+CircleOptimizer::Options *CircleOptimizer::options(void)
+{
+ if (_options == nullptr)
+ {
+ _options = std::make_unique<OptimizeOptionsImpl>();
+ }
+
+ return _options.get();
+}
+
+void CircleOptimizer::optimize(loco::Graph *g) const
+{
+ logo::Phase phase;
+
+ /* TRANSFORM DECLARATION BEGIN */
+ if (_options->query(Options::Algorithm::FuseInstanceNorm))
+ {
+ phase.emplace_back(std::make_unique<FuseInstanceNormPass>());
+ }
+ // Shape inference is needed for added nodes doing above transformations
+ phase.emplace_back(std::make_unique<luci::ShapeInferencePass>());
+ phase.emplace_back(std::make_unique<luci::TypeInferencePass>());
+ phase.emplace_back(std::make_unique<logo::RemoveDeadNodePass>());
+ /* 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 luci
diff --git a/compiler/luci/pass/src/FuseInstanceNormPass.cpp b/compiler/luci/pass/src/FuseInstanceNormPass.cpp
new file mode 100644
index 000000000..180b5bbef
--- /dev/null
+++ b/compiler/luci/pass/src/FuseInstanceNormPass.cpp
@@ -0,0 +1,401 @@
+/*
+ * 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 "luci/Pass/FuseInstanceNormPass.h"
+
+#include <luci/IR/CircleNodes.h>
+
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+#include <set>
+
+// Helper to find commutative node's arguments
+namespace
+{
+
+/**
+ * INTRODUCTION
+ * Binary operation f(x,y) is 'commutative' when
+ * f(x,y) == f(y,x) holds for all x, y.
+ * For examples, ADD, MUL and SQUARED_DIFFERENCE are commutative.
+ * These helpers make it easy to find commutative arguemnts of commtative node.
+ *
+ * HOW TO USE
+ * COMM_NODE *node;
+ * ARG_TYPE_1 *arg1;
+ * ARG_TYPE_2 *arg2;
+ *
+ * bool ok = fill(&arg1, &arg2).with_commutative_args_of(node);
+ *
+ * Result
+ * If 'node's commutative argument types are actually {ARG_TYPE_1, ARG_TYPE_2}
+ * (as a set), 'arg1' and 'arg2' set as actual 'node's arguemnts with matching
+ * type, and return value 'ok' is true.
+ * Otherwise, 'arg1' and 'arg2' not changed, 'ok' is false.
+ */
+
+template <class ARG_TYPE_1, class ARG_TYPE_2> class NodeFiller final
+{
+public:
+ NodeFiller(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2) : _arg_1(arg_1), _arg_2(arg_2)
+ {
+ // DO NOTHING
+ }
+
+ /**
+ * @return true When 'node's argument types are 'ARG_TYPE_1' and 'ARG_TYPE_2'
+ * In such case, it assign '_arg_1' and '_arg_2' to actual arguments
+ *
+ * @return false When 'node's argument types are NOT matched with 'ARG_TYPE_*'
+ * In such case, it does not amend '_arg_1' and '_arg_2'
+ *
+ * @require COMM_NODE has member x() and y()
+ */
+ template <class COMM_NODE> bool with_commutative_args_of(const COMM_NODE *node);
+
+private:
+ ARG_TYPE_1 **_arg_1;
+ ARG_TYPE_2 **_arg_2;
+};
+
+template <class ARG_TYPE_1, class ARG_TYPE_2>
+inline NodeFiller<ARG_TYPE_1, ARG_TYPE_2> fill(ARG_TYPE_1 **arg_1, ARG_TYPE_2 **arg_2)
+{
+ return NodeFiller<ARG_TYPE_1, ARG_TYPE_2>{arg_1, arg_2};
+}
+
+template <class ARG_TYPE_1, class ARG_TYPE_2>
+template <class COMM_NODE>
+bool NodeFiller<ARG_TYPE_1, ARG_TYPE_2>::with_commutative_args_of(const COMM_NODE *node)
+{
+ // Case 1) X == ARG_TYPE_1 / Y == ARG_TYPE_2
+ {
+ auto x = dynamic_cast<ARG_TYPE_1 *>(node->x());
+ auto y = dynamic_cast<ARG_TYPE_2 *>(node->y());
+
+ if (x && y)
+ {
+ *_arg_1 = x;
+ *_arg_2 = y;
+ return true;
+ }
+ }
+
+ // Case 2) X == ARG_TYPE_2 / Y == ARG_TYPE_1
+ {
+ auto x = dynamic_cast<ARG_TYPE_2 *>(node->x());
+ auto y = dynamic_cast<ARG_TYPE_1 *>(node->y());
+
+ if (x && y)
+ {
+ *_arg_1 = y;
+ *_arg_2 = x;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
+// Helper to check detail
+namespace
+{
+
+/// @return true When node has shape of '1 x .. x 1 x depth'
+bool is_1D_with_dummy_dim(luci::CircleConst *node, uint32_t depth)
+{
+ auto rank = node->rank();
+ uint32_t axis;
+ for (axis = 0; axis < rank - 1; ++axis)
+ {
+ if (node->dim(axis).value() != 1)
+ return false;
+ }
+ return node->dim(axis).value() == depth;
+}
+
+bool is_instance_mean(luci::CircleMean *mean)
+{
+ //
+ // CHECK 1) input is rank 4
+ //
+ auto input = mean->input();
+ if (not loco::shape_known(input))
+ return false;
+ auto input_shape = loco::shape_get(input).as<loco::TensorShape>();
+ if (input_shape.rank() != 4)
+ return false;
+
+ //
+ // CHECK 2) 'reduction indices' is CircleConst of value [1,2], that is HW of NHWC
+ //
+ // TODO Support equivalent case, like [-3,-2]
+ // TODO Support non-Const case?
+ // TODO What if input is NCHW format in Circle?
+ auto red_indices = dynamic_cast<luci::CircleConst *>(mean->reduction_indices());
+ if (not red_indices)
+ return false;
+ if (red_indices->rank() != 1)
+ return false;
+ std::set<int32_t> red_indices_set;
+ {
+ // TODO Currently only support S32, support other types
+ assert(red_indices->dtype() == loco::DataType::S32);
+ for (uint32_t i = 0; i < red_indices->dim(0).value(); ++i)
+ red_indices_set.insert(red_indices->at<loco::DataType::S32>(i));
+ }
+ if (red_indices_set.size() != 2)
+ return false;
+ if (red_indices_set.find(1) == red_indices_set.end())
+ return false;
+ if (red_indices_set.find(2) == red_indices_set.end())
+ return false;
+
+ //
+ // CHECK 3) keep_dims == true (?)
+ //
+ // We only have case of 'keep_dims == true' so far, but it might be okay with 'keep_dims == false'
+ // TODO Check this fact, and if true, return true regardless of keep_dims
+ return mean->keep_dims();
+}
+
+} // namespace
+
+// Helper to fuse Instance Norm
+namespace
+{
+
+/**
+ * SUBGRAPH PATTERN
+ *
+ * - Below diagram shows Instance Norm pattern to fuse.
+ * - Execution dependency order is top to the bottom.
+ * - Node name is matched with variable name of InstanceNormPattern class.
+ * - Usually, first word of node name (variable name) is node type. For e.g.
+ * variable 'mean_as_variance' is pointer to TFLMean.
+ * - (Item in parenthesis) means actually exist, but not having a name and
+ * not a variable of InstanceNormPattern class.
+ *
+ * TODO support other semantically same patterns for instance norm
+ *
+ * [In]
+ * |
+ * V
+ * +----------- ifm -----+ (reduction indicies)
+ * | | | |
+ * | | V V
+ * | | mean_of_ifm ----------------+
+ * | V | |
+ * | sqdiff <--+ (reduction indicies) |
+ * | | | |
+ * | V | |
+ * | mean_as_variance <---+ const_as_epsilon |
+ * | | | |
+ * | V | |
+ * | add_as_variance <--------+ |
+ * | | |
+ * | V |
+ * | rsqrt const_as_gamma |
+ * | | | |
+ * | V | |
+ * | mul_gamma <--+ |
+ * | | | |
+ * V V V |
+ * mul_as_scaled_ifm mul_as_scaled_mean <-------------+
+ * | |
+ * | const_as_beta |
+ * | | V
+ * | +------> sub
+ * V |
+ * add_as_terminal <----------+
+ * |
+ * V
+ * [Out]
+ */
+class InstanceNormPattern final
+{
+public:
+ InstanceNormPattern(luci::CircleAdd *candidate)
+ {
+ assert(candidate);
+ add_as_terminal = candidate;
+ }
+
+public:
+ bool matched();
+ bool matched() const { return _matched; }
+
+public:
+ // Context
+ loco::Node *ifm = nullptr;
+ luci::CircleMean *mean_of_ifm = nullptr;
+ luci::CircleSquaredDifference *sqdiff = nullptr;
+ luci::CircleMean *mean_as_variance = nullptr;
+ luci::CircleConst *const_as_epsilon = nullptr;
+ luci::CircleAdd *add_as_variance = nullptr;
+ luci::CircleRsqrt *rsqrt = nullptr;
+ luci::CircleConst *const_as_gamma = nullptr;
+ luci::CircleMul *mul_gamma = nullptr;
+ luci::CircleMul *mul_as_scaled_ifm = nullptr;
+ luci::CircleMul *mul_as_scaled_mean = nullptr;
+ luci::CircleConst *const_as_beta = nullptr;
+ luci::CircleSub *sub = nullptr;
+ luci::CircleAdd *add_as_terminal = nullptr;
+
+private:
+ bool _matched = false;
+};
+
+bool InstanceNormPattern::matched()
+{
+ if (_matched)
+ return true;
+
+#define CHECK_OR_FALSE(condition) \
+ if (not(condition)) \
+ return false;
+
+ // Check order is DFS
+
+ CHECK_OR_FALSE(fill(&mul_as_scaled_ifm, &sub).with_commutative_args_of(add_as_terminal));
+ CHECK_OR_FALSE(fill(&ifm, &mul_gamma).with_commutative_args_of(mul_as_scaled_ifm));
+
+ CHECK_OR_FALSE(loco::shape_known(ifm));
+ auto ifm_shape = loco::shape_get(ifm);
+ CHECK_OR_FALSE(ifm_shape.domain() == loco::Domain::Tensor);
+ auto ifm_tensor_shape = ifm_shape.as<loco::TensorShape>();
+ CHECK_OR_FALSE(ifm_tensor_shape.rank() == 4);
+ uint32_t ifm_channel_depth = ifm_tensor_shape.dim(3).value();
+
+ CHECK_OR_FALSE(fill(&rsqrt, &const_as_gamma).with_commutative_args_of(mul_gamma));
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_gamma, ifm_channel_depth));
+
+ add_as_variance = dynamic_cast<luci::CircleAdd *>(rsqrt->x());
+ CHECK_OR_FALSE(add_as_variance);
+
+ CHECK_OR_FALSE(
+ fill(&mean_as_variance, &const_as_epsilon).with_commutative_args_of(add_as_variance));
+
+ CHECK_OR_FALSE(const_as_epsilon->dtype() == loco::DataType::FLOAT32);
+ // TODO Support regarding broadcast
+ CHECK_OR_FALSE(const_as_epsilon->size<loco::DataType::FLOAT32>() == 1);
+
+ CHECK_OR_FALSE(is_instance_mean(mean_as_variance));
+ sqdiff = dynamic_cast<luci::CircleSquaredDifference *>(mean_as_variance->input());
+ CHECK_OR_FALSE(sqdiff);
+
+ loco::Node *ifm_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&ifm_should_be, &mean_of_ifm).with_commutative_args_of(sqdiff));
+ CHECK_OR_FALSE(ifm == ifm_should_be);
+ CHECK_OR_FALSE(is_instance_mean(mean_of_ifm));
+ CHECK_OR_FALSE(ifm == mean_of_ifm->input());
+
+ const_as_beta = dynamic_cast<luci::CircleConst *>(sub->x());
+ CHECK_OR_FALSE(const_as_beta);
+ CHECK_OR_FALSE(is_1D_with_dummy_dim(const_as_beta, ifm_channel_depth));
+
+ mul_as_scaled_mean = dynamic_cast<luci::CircleMul *>(sub->y());
+ CHECK_OR_FALSE(mul_as_scaled_mean);
+
+ luci::CircleMul *mul_gamma_should_be = nullptr;
+ luci::CircleMean *mean_of_ifm_should_be = nullptr;
+ CHECK_OR_FALSE(fill(&mul_gamma_should_be, &mean_of_ifm_should_be)
+ .with_commutative_args_of(mul_as_scaled_mean));
+ CHECK_OR_FALSE(mul_gamma == mul_gamma_should_be);
+ CHECK_OR_FALSE(mean_of_ifm == mean_of_ifm_should_be);
+#undef CHECK_OR_FALSE
+ _matched = true;
+ return true;
+}
+
+/**
+ * Instance norm pattern would be fused like following diagram:
+ *
+ * [In] --------------------------- CircleInstanceNorm --- [Out]
+ * / /
+ * const_as_gamma --- TFLReshape --- /
+ * /
+ * const_as_beta ---- TFLReshape ---
+ *
+ * Note
+ * - 'const_as_gamma' and 'const_as_beta' are from original graph
+ * - Value of 'const_as_epsilon' would be copied to CircleInstanceNorm's attribute
+ * - TFLReshape is added as CircleInstanceNorm only accept 1D tensor
+ * - 'CircleConst --- TFLReshape' is expected to be fused in constant folding for Reshape
+ */
+void fuse_instance_norm(const InstanceNormPattern &p)
+{
+ assert(p.matched());
+
+ auto graph = p.add_as_terminal->graph();
+
+ // Make reshape for gamma & beta
+ auto reshape_gamma = graph->nodes()->create<luci::CircleReshape>();
+ auto reshape_beta = graph->nodes()->create<luci::CircleReshape>();
+ {
+ auto ifm_shape = loco::shape_get(p.ifm).as<loco::TensorShape>();
+ uint32_t ifm_channel_depth = ifm_shape.dim(3).value();
+
+ int32_t new_shape[1] = {static_cast<int32_t>(ifm_channel_depth)};
+
+ reshape_gamma->tensor(p.const_as_gamma);
+ reshape_beta->tensor(p.const_as_beta);
+
+ luci::set_new_shape(reshape_gamma, new_shape, 1);
+ luci::set_new_shape(reshape_beta, new_shape, 1);
+ }
+
+ // Make Instance Norm to replace
+ auto instance_norm = graph->nodes()->create<luci::CircleInstanceNorm>();
+ instance_norm->input(p.ifm);
+ instance_norm->gamma(reshape_gamma);
+ instance_norm->beta(reshape_beta);
+ float epsilon = p.const_as_epsilon->at<loco::DataType::FLOAT32>(0);
+ instance_norm->epsilon(epsilon);
+ instance_norm->fusedActivationFunction(p.add_as_terminal->fusedActivationFunction());
+
+ replace(p.add_as_terminal).with(instance_norm);
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool FuseInstanceNormPass::run(loco::Graph *g)
+{
+ bool changed = false;
+ for (auto node : loco::active_nodes(loco::output_nodes(g)))
+ {
+ auto add = dynamic_cast<luci::CircleAdd *>(node);
+ if (not add)
+ continue;
+
+ InstanceNormPattern pattern(add);
+ if (not pattern.matched())
+ continue;
+
+ fuse_instance_norm(pattern);
+ changed = true;
+ }
+
+ return changed;
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ProgressReporter.cpp b/compiler/luci/pass/src/ProgressReporter.cpp
new file mode 100644
index 000000000..dcf47aba6
--- /dev/null
+++ b/compiler/luci/pass/src/ProgressReporter.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "ProgressReporter.h"
+
+#include <luci/Log.h>
+#include <luci/LogHelper.h>
+
+#include <logo/Phase.h>
+#include <logo/Pass.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 luci
+{
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseBegin> *)
+{
+ LOGGER(prime);
+
+ INFO(prime) << "==============================================================";
+ INFO(prime) << "PhaseRunner<" << to_str(strategy()) << ">";
+ INFO(prime) << "Initial graph";
+ INFO(prime) << luci::fmt(graph());
+}
+
+void ProgressReporter::notify(const logo::PhaseEventInfo<logo::PhaseEvent::PhaseEnd> *)
+{
+ 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) << luci::fmt(graph());
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/ProgressReporter.h b/compiler/luci/pass/src/ProgressReporter.h
new file mode 100644
index 000000000..bd2ba9849
--- /dev/null
+++ b/compiler/luci/pass/src/ProgressReporter.h
@@ -0,0 +1,53 @@
+/*
+ * 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 __LUCI_PROGRESSREPORTER_H__
+#define __LUCI_PROGRESSREPORTER_H__
+
+#include <logo/Phase.h>
+
+#include <loco.h>
+
+namespace luci
+{
+
+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 luci
+
+#endif // __LUCI_PROGRESSREPORTER_H__
diff --git a/compiler/luci/pass/src/ShapeInferencePass.cpp b/compiler/luci/pass/src/ShapeInferencePass.cpp
new file mode 100644
index 000000000..f681b3d5f
--- /dev/null
+++ b/compiler/luci/pass/src/ShapeInferencePass.cpp
@@ -0,0 +1,44 @@
+/*
+ * 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 "luci/Pass/ShapeInferencePass.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/Service/CircleShapeInferenceRule.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+namespace luci
+{
+
+bool ShapeInferencePass::run(loco::Graph *g)
+{
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ luci::CircleShapeInferenceRule circle_rule;
+
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &circle_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace luci
diff --git a/compiler/luci/pass/src/TypeInferencePass.cpp b/compiler/luci/pass/src/TypeInferencePass.cpp
new file mode 100644
index 000000000..2c7b3a897
--- /dev/null
+++ b/compiler/luci/pass/src/TypeInferencePass.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "luci/Pass/TypeInferencePass.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/Service/CircleTypeInferenceRule.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+namespace luci
+{
+
+bool TypeInferencePass::run(loco::Graph *g)
+{
+ loco::CanonicalTypeInferenceRule canonical_rule;
+ luci::CircleTypeInferenceRule circle_rule;
+
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &circle_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace luci
diff --git a/compiler/luci/requires.cmake b/compiler/luci/requires.cmake
new file mode 100644
index 000000000..e88dabd24
--- /dev/null
+++ b/compiler/luci/requires.cmake
@@ -0,0 +1,9 @@
+require("loco")
+require("locop")
+require("logo-core")
+require("mio-circle")
+require("oops")
+require("hermes")
+require("hermes-std")
+require("tflchef")
+require("tflite2circle")
diff --git a/compiler/luci/service/CMakeLists.txt b/compiler/luci/service/CMakeLists.txt
new file mode 100644
index 000000000..9f50c9c4f
--- /dev/null
+++ b/compiler/luci/service/CMakeLists.txt
@@ -0,0 +1,25 @@
+file(GLOB_RECURSE SOURCES "src/*.cpp")
+file(GLOB_RECURSE TESTS "src/*.test.cpp")
+list(REMOVE_ITEM SOURCES ${TESTS})
+
+add_library(luci_service SHARED ${SOURCES})
+target_include_directories(luci_service PRIVATE src)
+target_include_directories(luci_service PUBLIC include)
+target_link_libraries(luci_service PUBLIC luci_lang)
+target_link_libraries(luci_service PUBLIC mio_circle)
+target_link_libraries(luci_service PUBLIC logo_core)
+target_link_libraries(luci_service PRIVATE luci_log)
+target_link_libraries(luci_service PRIVATE nncc_common)
+target_link_libraries(luci_service PRIVATE oops)
+install(TARGETS luci_service DESTINATION lib)
+
+if(NOT ENABLE_TEST)
+ return()
+endif(NOT ENABLE_TEST)
+
+nnas_find_package(GTest REQUIRED)
+
+GTest_AddTest(luci_service_test ${TESTS})
+target_include_directories(luci_service_test PRIVATE src)
+target_link_libraries(luci_service_test luci_service)
+target_link_libraries(luci_service_test oops)
diff --git a/compiler/luci/service/README.md b/compiler/luci/service/README.md
new file mode 100644
index 000000000..ac3583145
--- /dev/null
+++ b/compiler/luci/service/README.md
@@ -0,0 +1,3 @@
+# luci-service
+
+_luci-service_ provides Circle Dialect Services
diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInference.h b/compiler/luci/service/include/luci/Service/CircleShapeInference.h
new file mode 100644
index 000000000..fb934c2cf
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleShapeInference.h
@@ -0,0 +1,41 @@
+/*
+ * 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 __LUCI_CIRCLE_SHAPE_INFERENCE_H__
+#define __LUCI_CIRCLE_SHAPE_INFERENCE_H__
+
+#include "ShapeDescription.h"
+
+#include <loco/IR/Nodes.h>
+
+namespace luci
+{
+
+/**
+ * @brief Get the shape of each node as a node annotation
+ *
+ * HOW TO USE
+ *
+ * ShapeInference::get(g->nodes()->at(..));
+ */
+struct ShapeInference
+{
+ static ShapeDescription get(loco::Node *node);
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_SHAPE_INFERENCE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h
new file mode 100644
index 000000000..3f63c9633
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleShapeInferenceRule.h
@@ -0,0 +1,33 @@
+/*
+ * 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 __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__
+#define __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__
+
+#include <loco/Service/ShapeInference.h>
+
+namespace luci
+{
+
+struct CircleShapeInferenceRule final : public loco::ShapeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::NodeShape &) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_SHAPE_INFERENCE_RULE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInference.h b/compiler/luci/service/include/luci/Service/CircleTypeInference.h
new file mode 100644
index 000000000..ea7a3c5ed
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleTypeInference.h
@@ -0,0 +1,42 @@
+/*
+ * 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 __LUCI_CIRCLE_TYPE_INFERENCE_H__
+#define __LUCI_CIRCLE_TYPE_INFERENCE_H__
+
+#include <loco/IR/Nodes.h>
+
+#include <mio/circle/schema_generated.h>
+
+namespace luci
+{
+
+/**
+ * @brief Get the type of each node as NodeAnnotation
+ *
+ * HOW TO USE
+ *
+ * TypeInference::get(g->nodes()->at(0));
+ * TypeInference::get(g->nodes()->at(...));
+ */
+struct TypeInference
+{
+ static circle::TensorType get(loco::Node *node);
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_TYPE_INFERENCE_H__
diff --git a/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h b/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h
new file mode 100644
index 000000000..3b21081ef
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/CircleTypeInferenceRule.h
@@ -0,0 +1,36 @@
+/*
+ * 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 __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__
+#define __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__
+
+#include <loco/Service/TypeInference.h>
+
+namespace luci
+{
+
+/**
+ * @brief Type Inference Rule for CircleDialect
+ */
+struct CircleTypeInferenceRule final : public loco::TypeInferenceRule
+{
+ bool recognize(const loco::Dialect *) const final;
+ bool infer(const loco::Node *, loco::DataType &) const final;
+};
+
+} // namespace luci
+
+#endif // __LUCI_CIRCLE_TYPE_INFERENCE_RULE_H__
diff --git a/compiler/luci/service/include/luci/Service/ShapeDescription.h b/compiler/luci/service/include/luci/Service/ShapeDescription.h
new file mode 100644
index 000000000..949cce535
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/ShapeDescription.h
@@ -0,0 +1,59 @@
+/*
+ * 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 __LUCI_SHAPE_DESCRIPTION_H__
+#define __LUCI_SHAPE_DESCRIPTION_H__
+
+#include <loco/IR/PermutingCodec.h>
+#include <loco/IR/NodeShape.h>
+
+#include <cstdint>
+#include <vector>
+
+namespace luci
+{
+
+struct ShapeDescription
+{
+ std::vector<int32_t> _dims;
+ bool _rank_known;
+};
+
+// TODO remove these when CircleDialect is fully functioal
+ShapeDescription to_shape_description(const loco::TensorShape &shape);
+ShapeDescription to_shape_description(const loco::FeatureShape &shape);
+ShapeDescription to_shape_description(const loco::FilterShape &shape);
+ShapeDescription to_shape_description(const loco::BiasShape &shape);
+ShapeDescription to_shape_description(const loco::MatrixShape &shape);
+ShapeDescription to_shape_description(const loco::NodeShape &shape);
+
+template <typename Permutation> inline bool isNHWC(Permutation *perm);
+
+template <> inline bool isNHWC(loco::Permutation<loco::Domain::Feature> *perm)
+{
+ return perm->axis(loco::FeatureAxis::Count) == 0 && perm->axis(loco::FeatureAxis::Height) == 1 &&
+ perm->axis(loco::FeatureAxis::Width) == 2 && perm->axis(loco::FeatureAxis::Depth) == 3;
+}
+
+template <> inline bool isNHWC(loco::Permutation<loco::Domain::Filter> *perm)
+{
+ return perm->axis(loco::FilterAxis::Count) == 0 && perm->axis(loco::FilterAxis::Height) == 1 &&
+ perm->axis(loco::FilterAxis::Width) == 2 && perm->axis(loco::FilterAxis::Depth) == 3;
+}
+
+} // namespace luci
+
+#endif // __LUCI_SHAPE_DESCRIPTION_H__
diff --git a/compiler/luci/service/include/luci/Service/Validate.h b/compiler/luci/service/include/luci/Service/Validate.h
new file mode 100644
index 000000000..4b80d1d16
--- /dev/null
+++ b/compiler/luci/service/include/luci/Service/Validate.h
@@ -0,0 +1,29 @@
+/*
+ * 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 __LUCI_SERVICE_VALIDATE_H__
+#define __LUCI_SERVICE_VALIDATE_H__
+
+#include <loco.h>
+
+namespace luci
+{
+
+bool validate(loco::Graph *);
+
+} // namespace luci
+
+#endif // __LUCI_SERVICE_VALIDATE_H__
diff --git a/compiler/luci/service/src/Check.h b/compiler/luci/service/src/Check.h
new file mode 100644
index 000000000..e05ec904a
--- /dev/null
+++ b/compiler/luci/service/src/Check.h
@@ -0,0 +1,35 @@
+/*
+ * 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 __CHECK_H__
+#define __CHECK_H__
+
+#include <stdexcept>
+#include <cassert>
+#include <iostream>
+
+// TODO Add macro for Release version
+
+#define LUCI_ASSERT(condition, msg) \
+ { \
+ if (!(condition)) \
+ { \
+ std::cerr << "[assert failed] " << (msg) << ". " << std::endl; \
+ assert((condition)); \
+ } \
+ }
+
+#endif // __CHECK_H__
diff --git a/compiler/luci/service/src/CircleShapeInference.cpp b/compiler/luci/service/src/CircleShapeInference.cpp
new file mode 100644
index 000000000..fdcfa76bc
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeInference.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "luci/Service/CircleShapeInference.h"
+#include "luci/Service/ShapeDescription.h"
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+ShapeDescription ShapeInference::get(loco::Node *node)
+{
+ // TODO Adjust indentation level
+ {
+ assert(loco::shape_known(node));
+ return to_shape_description(loco::shape_get(node));
+ }
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.cpp
new file mode 100644
index 000000000..c8e872b1e
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeInferenceRule.cpp
@@ -0,0 +1,907 @@
+/*
+ * 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 "luci/Service/CircleShapeInferenceRule.h"
+#include "Check.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/Log.h>
+
+#include <oops/InternalExn.h>
+
+#include <algorithm>
+#include <cassert>
+#include <stdexcept>
+
+namespace
+{
+
+// Call this for CircleAvgPool2D and CircleMaxPool2D only
+template <class Pool2DType> loco::NodeShape infer_pool_2d_shape(const Pool2DType *node)
+{
+ LUCI_ASSERT(loco::shape_known(node->value()), "Shape must be known");
+
+ auto ifm_shape = loco::shape_get(node->value()).template as<loco::TensorShape>();
+ assert(ifm_shape.rank() == 4);
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t window_height = node->filter()->h();
+ uint32_t window_width = node->filter()->w();
+ uint32_t dilation_height = 1; // dilation for CircleAvgPool2D and CircleMaxPool2D is 1
+ uint32_t dilation_width = 1;
+ uint32_t effective_window_height = dilation_height * (window_height - 1) + 1;
+ uint32_t effective_window_width = dilation_width * (window_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == luci::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_window_height) / stride_height;
+ output_width = (input_width + stride_width - effective_window_width) / stride_width;
+ }
+ else if (node->padding() == luci::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ LUCI_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ifm_shape.dim(3);
+
+ return loco::NodeShape{ofm_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;
+};
+
+/**
+ * @breif Expand shape x and y to same rank by align right and filling with 1
+ */
+void expand_rank(loco::TensorShape &x, loco::TensorShape &y)
+{
+ auto x_rank = x.rank();
+ auto y_rank = y.rank();
+
+ if (x_rank == y_rank)
+ return;
+
+ TensorShapeExpander x_exp(x);
+ TensorShapeExpander y_exp(y);
+
+ auto xy_rank = std::max(x_rank, y_rank);
+
+ x = x_rank > y_rank ? x : x_exp.to(xy_rank);
+ y = y_rank > x_rank ? y : y_exp.to(xy_rank);
+}
+
+/**
+ * @breif Returns shape of expanded dimension of input x and y having same rank
+ */
+loco::TensorShape expand_dimension(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ assert(x.rank() == y.rank());
+
+ auto rank = x.rank();
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(rank);
+ for (uint32_t axis = 0; axis < rank; ++axis)
+ {
+ assert(x.dim(axis).known() && y.dim(axis).known());
+
+ auto x_dim = x.dim(axis).value();
+ auto y_dim = y.dim(axis).value();
+
+ // each dimension of x and y should be same or one must be 1 if different
+ if (!((x_dim == y_dim) || (x_dim == 1 || y_dim == 1)))
+ INTERNAL_EXN("Cannot produce expand_dimension of two shapes");
+
+ output_shape.dim(axis) = std::max(x_dim, y_dim);
+ }
+
+ return output_shape;
+}
+
+loco::TensorShape broadcast_shape(const loco::TensorShape &x, const loco::TensorShape &y)
+{
+ auto x_match = x;
+ auto y_match = y;
+
+ expand_rank(x_match, y_match);
+
+ auto output_shape = expand_dimension(x_match, y_match);
+
+ return output_shape;
+}
+
+/**
+ * @brief Class to infer the shape of CircleNode
+ *
+ * @note All CircleNode's inputs and outputs are always loco::Domain::Tensor
+ */
+class ShapeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::NodeShape>
+{
+public:
+ loco::NodeShape visit(const luci::CircleAbs *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ return loco::NodeShape{x_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleAdd *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleArgMax *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto dimension_shape = loco::shape_get(node->dimension()).as<loco::TensorShape>();
+
+ int64_t select_axis = 0;
+ {
+ LUCI_ASSERT(node->dimension(), "2nd input dimension() should not be nullptr");
+
+ // Only support node's shape() is CircleConst with S32/S64
+ // Support S32 for now.
+ auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->dimension());
+ LUCI_ASSERT(const_shape_node, "Only support CircleConst for shape of CircleArgMax");
+ LUCI_ASSERT(const_shape_node->dtype() == loco::DataType::S32,
+ "Only support int32 CircleConst for CircleArgMax");
+
+ if (const_shape_node->rank() > 1)
+ INTERNAL_EXN_V("Only support rank 0/1 CircleConst",
+ oops::to_uint32(const_shape_node->rank()));
+
+ select_axis = const_shape_node->scalar<loco::DataType::S32>();
+ }
+ assert(select_axis < input_shape.rank());
+ assert(select_axis >= 0); // TODO support minus of this breaks
+
+ // NOTE select_axis is removed
+ loco::TensorShape shape_output;
+ uint32_t rank = input_shape.rank();
+ uint32_t shrink = static_cast<uint32_t>(select_axis);
+ assert(rank > 0);
+ shape_output.rank(rank - 1);
+ for (uint32_t r = 0, d = 0; r < rank; ++r)
+ {
+ if (r == shrink)
+ continue;
+ shape_output.dim(d++) = input_shape.dim(r);
+ }
+ return loco::NodeShape{shape_output};
+ }
+
+ loco::NodeShape visit(const luci::CircleAveragePool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleBatchToSpaceND *node) final
+ {
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ // Support only input rank is 3 and 4
+ assert(input_shape.rank() == 3 || input_shape.rank() == 4);
+
+ // Only support block_shape() with S32 type CircleConst for now
+ auto const_block_shape = dynamic_cast<luci::CircleConst *>(node->block_shape());
+ LUCI_ASSERT(const_block_shape, "Only support CircleConst for block_shape");
+ LUCI_ASSERT(const_block_shape->dtype() == loco::DataType::S32,
+ "Only support int32 block_shape");
+
+ // Only support crops() with S32 type CircleConst for now
+ auto const_crops = dynamic_cast<luci::CircleConst *>(node->crops());
+ LUCI_ASSERT(const_crops, "Only support CircleConst for crops");
+ LUCI_ASSERT(const_crops->dtype() == loco::DataType::S32, "Only support int32 crops");
+
+ auto const_block_shape_shape = loco::shape_get(const_block_shape).as<loco::TensorShape>();
+ auto const_crops_shape = loco::shape_get(const_crops).as<loco::TensorShape>();
+ assert(const_block_shape_shape.rank() == 1);
+ assert(const_crops_shape.rank() == 2);
+
+ int32_t input_spatial_dim = input_shape.rank() - 2;
+ assert(const_block_shape_shape.dim(0) == input_spatial_dim);
+ assert(const_crops_shape.dim(0) == input_spatial_dim);
+ assert(const_crops_shape.dim(1) == 2);
+
+ loco::TensorShape shape_output;
+
+ shape_output.rank(input_shape.rank());
+
+ int32_t output_batch_size = input_shape.dim(0).value();
+ for (int32_t dim = 0; dim < input_spatial_dim; ++dim)
+ {
+ int dim_size = input_shape.dim(dim + 1).value() * const_block_shape->at<S32>(dim);
+ dim_size -= const_crops->at<S32>(dim * 2);
+ dim_size -= const_crops->at<S32>(dim * 2 + 1);
+ shape_output.dim(dim + 1) = dim_size;
+
+ assert(output_batch_size % const_block_shape->at<S32>(dim) == 0);
+ output_batch_size = output_batch_size / const_block_shape->at<S32>(dim);
+ }
+ shape_output.dim(0) = output_batch_size;
+ shape_output.dim(input_shape.rank() - 1) = input_shape.dim(input_shape.rank() - 1);
+
+ return loco::NodeShape{shape_output};
+ }
+
+ loco::NodeShape visit(const luci::CircleConcatenation *node) final
+ {
+ // TODO Support when CircleConcatenation has 0 input
+ assert(node->numValues() > 0);
+
+ auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>();
+ auto axis = node->axis();
+ if (axis < 0)
+ axis += first_shape.rank();
+
+ assert(0 <= axis);
+ assert(first_shape.rank() > static_cast<uint32_t>(axis));
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(first_shape.rank());
+ for (uint32_t i = 0; i < output_shape.rank(); ++i)
+ output_shape.dim(i) = first_shape.dim(i);
+
+ for (uint32_t i = 1; i < node->numValues(); ++i)
+ {
+ auto input_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>();
+
+ for (uint32_t j = 0; j < output_shape.rank(); ++j)
+ {
+ if (j == static_cast<uint32_t>(axis))
+ output_shape.dim(j) = output_shape.dim(j).value() + input_shape.dim(j).value();
+ else
+ assert(output_shape.dim(j) == input_shape.dim(j));
+ }
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleConst *node) final
+ {
+ loco::TensorShape shape;
+
+ shape.rank(node->rank());
+ for (uint32_t axis = 0; axis < node->rank(); axis++)
+ shape.dim(axis) = node->dim(axis);
+
+ return loco::NodeShape{shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleConv2D *node) final
+ {
+ LOGGER(l);
+
+ auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
+ auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in OHWI
+
+ INFO(l) << "[luci] CircleConv2D ShapeInf ifm(" << ifm_shape.rank() << ") ker("
+ << ker_shape.rank() << ")" << std::endl;
+
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+ assert(ifm_shape.dim(3) == ker_shape.dim(3));
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t ker_height = ker_shape.dim(1).value();
+ uint32_t ker_width = ker_shape.dim(2).value();
+ uint32_t dilation_height = 1;
+ uint32_t dilation_width = 1;
+ uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
+ uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == luci::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_ker_height) / stride_height;
+ output_width = (input_width + stride_width - effective_ker_width) / stride_width;
+ }
+ else if (node->padding() == luci::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ LUCI_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ker_shape.dim(0);
+
+ return loco::NodeShape{ofm_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleCos *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+
+ return loco::NodeShape{x_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleDepthwiseConv2D *node) final
+ {
+ auto ifm_shape = loco::shape_get(node->input()).as<loco::TensorShape>(); // in NHWC
+ auto ker_shape = loco::shape_get(node->filter()).as<loco::TensorShape>(); // in 1 H W CM
+
+ assert(ifm_shape.rank() == 4);
+ assert(ker_shape.rank() == 4);
+ assert(ker_shape.dim(0).value() == 1);
+
+ uint32_t input_height = ifm_shape.dim(1).value();
+ uint32_t input_width = ifm_shape.dim(2).value();
+ uint32_t stride_height = node->stride()->h();
+ uint32_t stride_width = node->stride()->w();
+ uint32_t ker_height = ker_shape.dim(1).value();
+ uint32_t ker_width = ker_shape.dim(2).value();
+ uint32_t dilation_height = 1;
+ uint32_t dilation_width = 1;
+ uint32_t effective_ker_height = dilation_height * (ker_height - 1) + 1;
+ uint32_t effective_ker_width = dilation_width * (ker_width - 1) + 1;
+
+ uint32_t output_height = 0;
+ uint32_t output_width = 0;
+
+ if (node->padding() == luci::Padding::VALID)
+ {
+ output_height = (input_height + stride_height - effective_ker_height) / stride_height;
+ output_width = (input_width + stride_width - effective_ker_width) / stride_width;
+ }
+ else if (node->padding() == luci::Padding::SAME)
+ {
+ output_height = (input_height + stride_height - 1) / stride_height;
+ output_width = (input_width + stride_width - 1) / stride_width;
+ }
+ else
+ LUCI_ASSERT(false, "Wrong padding type");
+
+ loco::TensorShape ofm_shape;
+ ofm_shape.rank(4);
+ ofm_shape.dim(0) = ifm_shape.dim(0);
+ ofm_shape.dim(1) = output_height;
+ ofm_shape.dim(2) = output_width;
+ ofm_shape.dim(3) = ker_shape.dim(3);
+
+ return loco::NodeShape{ofm_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleDiv *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleEqual *node) final
+ {
+ const auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ const auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+ loco::TensorShape output_shape = broadcast_shape(x_shape, y_shape);
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleExp *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ return loco::NodeShape{x_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleFullyConnected *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto weights_shape = loco::shape_get(node->weights()).as<loco::TensorShape>();
+
+ // Checking shape capability for fully connected layer
+ // Input: a tensor of at least rank 2 [D1, D2, ... Dn]
+ // Weight: [# of units, K]
+ // Output: [D1 * D2 * ... * Dn / K, # of units]
+ LUCI_ASSERT(input_shape.rank() >= 2, "Input rank should be at least 2");
+ LUCI_ASSERT(weights_shape.rank() == 2, "Incompatible weights rank for fully connected");
+
+ uint32_t input_size = 1;
+ for (uint32_t i = 0; i < input_shape.rank(); i++)
+ {
+ input_size = input_size * input_shape.dim(i).value();
+ }
+ const uint32_t batch_size = input_size / weights_shape.dim(1).value();
+ loco::TensorShape out_shape;
+ out_shape.rank(2);
+ out_shape.dim(0) = batch_size;
+ out_shape.dim(1) = weights_shape.dim(0);
+
+ return loco::NodeShape{out_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleLogicalNot *node) final
+ {
+ const auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleLogicalOr *node) final
+ {
+ const auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleMaximum *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleMaxPool2D *node) final
+ {
+ return infer_pool_2d_shape(node);
+ }
+
+ loco::NodeShape visit(const luci::CircleMean *node) final
+ {
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto reduction_indices = dynamic_cast<luci::CircleConst *>(node->reduction_indices());
+
+ { // Exceptions
+ // TODO support non-const case
+ LUCI_ASSERT(reduction_indices, "Only support constant reduction_indices");
+ // TODO support other data type
+ LUCI_ASSERT(reduction_indices->dtype() == S32, "Only support int 32");
+ }
+
+ std::vector<int32_t> reduction_values;
+
+ for (uint32_t i = 0; i < reduction_indices->size<S32>(); ++i)
+ {
+ int32_t axis = reduction_indices->at<S32>(i);
+ if (axis < 0)
+ axis += input_shape.rank();
+ if (not(0 <= axis and axis < static_cast<int32_t>(input_shape.rank())))
+ INTERNAL_EXN_V("Invalid reduction axis for MEAN", oops::to_uint32(axis));
+ reduction_values.push_back(axis);
+ }
+
+ loco::TensorShape output_shape;
+
+ if (node->keep_dims())
+ {
+ output_shape.rank(input_shape.rank());
+ for (uint32_t i = 0; i < input_shape.rank(); ++i)
+ output_shape.dim(i) = input_shape.dim(i);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ output_shape.dim(reduction_values.at(i)) = 1;
+ }
+ else
+ {
+ std::vector<bool> check_reduce(input_shape.rank(), false);
+ for (uint32_t i = 0; i < reduction_values.size(); ++i)
+ check_reduce.at(reduction_values.at(i)) = true;
+
+ uint32_t reduce_cnt = 0;
+ for (uint32_t i = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i))
+ ++reduce_cnt;
+
+ output_shape.rank(input_shape.rank() - reduce_cnt);
+ for (uint32_t i = 0, j = 0; i < check_reduce.size(); ++i)
+ if (check_reduce.at(i) == false)
+ output_shape.dim(j++) = i;
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleMul *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CirclePack *node) final
+ {
+ LUCI_ASSERT(node->values_count() > 0, "Only support one or more inputs");
+
+ auto first_shape = loco::shape_get(node->values(0)).as<loco::TensorShape>();
+ // Make sure all inputs have the same shape.
+ for (uint32_t i = 1; i < node->values_count(); ++i)
+ {
+ auto in_shape = loco::shape_get(node->values(i)).as<loco::TensorShape>();
+ LUCI_ASSERT(loco::NodeShape{first_shape} == loco::NodeShape{in_shape},
+ "All inputs must have the same shape");
+ }
+
+ // Checking shape capability for pack layer
+ // Input: tensors [D1, D2, ... Dn]
+ // Axis: K
+ // Output: [D1, D2, ... , D_K-1, n, D_K+1, ... Dn]
+ auto axis = node->axis();
+ if (axis < 0)
+ axis += first_shape.rank() + 1;
+
+ LUCI_ASSERT(0 <= axis, "Axis is out of range");
+ LUCI_ASSERT(static_cast<uint32_t>(axis) <= first_shape.rank(), "Axis is out of range");
+
+ loco::TensorShape output_shape;
+ output_shape.rank(first_shape.rank() + 1);
+
+ uint32_t j = 0;
+ for (uint32_t i = 0; i < output_shape.rank(); ++i)
+ {
+ if (i == static_cast<uint32_t>(axis))
+ {
+ output_shape.dim(i) = node->values_count();
+ }
+ else
+ {
+ output_shape.dim(i) = first_shape.dim(j++);
+ }
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CirclePad *node) final
+ {
+ const loco::DataType S32 = loco::DataType::S32;
+
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+ auto paddings = dynamic_cast<luci::CircleConst *>(node->paddings());
+
+ // TODO support non-const case
+ LUCI_ASSERT(paddings, "Only support constant reduction_indices");
+ // TODO support other data type
+ LUCI_ASSERT(paddings->dtype() == S32, "Only support int 32 for now");
+ LUCI_ASSERT(paddings->rank() == 2, "paddings should be rank 2")
+
+ int32_t n = paddings->dim(0).value();
+ int32_t v = paddings->dim(1).value();
+
+ LUCI_ASSERT(v == 2, "paddings should be [n, 2]");
+ LUCI_ASSERT(n == int32_t(input_shape.rank()),
+ "paddings [n, 2] should have same value of input rank");
+
+ loco::TensorShape output_shape;
+
+ output_shape.rank(input_shape.rank());
+ for (int32_t ni = 0; ni < n; ++ni)
+ {
+ int32_t idx = ni * 2;
+ int value = input_shape.dim(ni).value();
+ value += paddings->at<S32>(idx + 0); // left
+ value += paddings->at<S32>(idx + 1); // right
+ output_shape.dim(ni) = value;
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRelu *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRelu6 *node) final
+ {
+ auto input_shape = loco::shape_get(node->features()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ /**
+ * @note CircleReshape has new shape info in two places: 2nd input and attribute.
+ * This shape inference forces both to exist, and match each other.
+ * When this condition satisfied, it return the inferred shape
+ *
+ * TODO Change this policy when not appropriate
+ */
+ loco::NodeShape visit(const luci::CircleReshape *node) final
+ {
+ const loco::DataType S32 = loco::DataType::S32;
+
+ loco::TensorShape shape_by_input;
+ {
+ LUCI_ASSERT(node->shape(), "2nd input shape() should not be nullptr");
+
+ // Only support node's shape() is CircleConst with S32
+ // TODO support other node with other types
+ auto const_shape_node = dynamic_cast<luci::CircleConst *>(node->shape());
+ LUCI_ASSERT(const_shape_node, "Only support CircleConst for shape of CircleReshape");
+ LUCI_ASSERT(const_shape_node->dtype() == S32, "Only support int32 CircleConst");
+
+ if (const_shape_node->rank() != 1)
+ INTERNAL_EXN_V("Only support rank 1 CircleConst",
+ oops::to_uint32(const_shape_node->rank()));
+
+ shape_by_input.rank(const_shape_node->dim(0).value());
+
+ for (uint32_t axis = 0; axis < shape_by_input.rank(); ++axis)
+ {
+ shape_by_input.dim(axis) = const_shape_node->at<S32>(axis);
+ }
+ }
+
+ loco::TensorShape shape_by_attr;
+ {
+ shape_by_attr.rank(node->newShape()->rank());
+
+ for (uint32_t axis = 0; axis < shape_by_attr.rank(); ++axis)
+ {
+ shape_by_attr.dim(axis) = node->newShape()->dim(axis);
+ }
+ }
+
+ LUCI_ASSERT(shape_by_input == shape_by_attr,
+ "Warning: Two new shape information mismatched for CircleReshape");
+
+ loco::TensorShape output_shape = shape_by_input;
+
+ // One of the dimensions can have special value -1, meaning its actual value should be inferred.
+ const auto input_shape = loco::shape_get(node->tensor()).as<loco::TensorShape>();
+ const uint32_t input_element_count = loco::element_count(&input_shape);
+ uint32_t output_element_count = 1;
+ uint32_t unknown_dim_index = UINT32_MAX;
+ for (uint32_t dim_index = 0; dim_index < output_shape.rank(); ++dim_index)
+ {
+ const uint32_t dim_value = output_shape.dim(dim_index).value();
+ if (static_cast<int>(dim_value) == -1)
+ {
+ LUCI_ASSERT(unknown_dim_index == UINT32_MAX, "More than one unknown dimension");
+ unknown_dim_index = dim_index;
+ }
+ else
+ {
+ output_element_count *= dim_value;
+ }
+ }
+ if (unknown_dim_index != UINT32_MAX)
+ {
+ output_shape.dim(unknown_dim_index) = input_element_count / output_element_count;
+ }
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleRsqrt *node) final
+ {
+ auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleSoftmax *node) final
+ {
+ auto input_shape = loco::shape_get(node->logits()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleSqrt *node) final
+ {
+ auto input_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleSquaredDifference *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleSub *node) final
+ {
+ auto x_shape = loco::shape_get(node->x()).as<loco::TensorShape>();
+ auto y_shape = loco::shape_get(node->y()).as<loco::TensorShape>();
+
+ auto output_shape = broadcast_shape(x_shape, y_shape);
+
+ return loco::NodeShape{output_shape};
+ }
+
+ // TODO CircleTanh
+
+ /// @brief Returns output shape of transpose. Use loco::ConstGen and luci::CircleConst for ConstT.
+ template <class ConstT>
+ loco::TensorShape output_shape_of_transpose(loco::TensorShape input_shape,
+ const ConstT *perm_node)
+ {
+ loco::TensorShape output_shape;
+ output_shape.rank(input_shape.rank());
+
+ assert(perm_node->dtype() == loco::DataType::S32);
+ assert(input_shape.rank() == perm_node->template size<loco::DataType::S32>());
+
+ for (uint32_t out_axis = 0; out_axis < output_shape.rank(); out_axis++)
+ {
+ auto in_axis = perm_node->template at<loco::DataType::S32>(out_axis);
+ output_shape.dim(out_axis) = input_shape.dim(in_axis);
+ }
+
+ return output_shape;
+ }
+
+ loco::NodeShape visit(const luci::CircleTranspose *node) final
+ {
+ auto input_shape = loco::shape_get(node->a()).as<loco::TensorShape>();
+
+ auto canon_perm = dynamic_cast<loco::ConstGen *>(node->perm());
+ auto circle_perm = dynamic_cast<luci::CircleConst *>(node->perm());
+
+ if (canon_perm)
+ {
+ return loco::NodeShape{output_shape_of_transpose(input_shape, canon_perm)};
+ }
+ else if (circle_perm)
+ {
+ return loco::NodeShape{output_shape_of_transpose(input_shape, circle_perm)};
+ }
+ else
+ INTERNAL_EXN("perm of CircleTranspose should be either ConstGen or CircleConst");
+ }
+
+ loco::NodeShape visit(const luci::CircleTransposeConv *node) final
+ {
+ // TransposeConv's output shape is written in its 'inputSizes' argument
+ auto input_sizes_const = dynamic_cast<luci::CircleConst *>(node->inputSizes());
+ LUCI_ASSERT(input_sizes_const,
+ "Only support when CircleTransposeConv's inputSizes is CircleConst")
+ LUCI_ASSERT(input_sizes_const->dtype() == loco::DataType::S32, "Only support S32 dtype")
+ LUCI_ASSERT(input_sizes_const->rank() == 1 && input_sizes_const->dim(0).value() == 4,
+ "Only support rank 1 with 4 entries")
+
+ loco::TensorShape shape;
+
+ shape.rank(4);
+ for (uint32_t axis = 0; axis < 4; ++axis)
+ shape.dim(axis) = input_sizes_const->at<loco::DataType::S32>(axis);
+
+ return loco::NodeShape{shape};
+ }
+
+ // Circle Only
+ loco::NodeShape visit(const luci::CircleInstanceNorm *node) final
+ {
+ auto input_shape = loco::shape_get(node->input()).as<loco::TensorShape>();
+
+ return loco::NodeShape{input_shape};
+ }
+
+ // Virtual
+ loco::NodeShape visit(const luci::CircleInput *node) final
+ {
+ loco::TensorShape shape;
+
+ shape.rank(node->rank());
+ for (uint32_t axis = 0; axis < node->rank(); axis++)
+ shape.dim(axis) = node->dim(axis);
+
+ return loco::NodeShape{shape};
+ }
+
+ loco::NodeShape visit(const luci::CircleOutput *node) final
+ {
+ auto from_shape = loco::shape_get(node->from()).as<loco::TensorShape>();
+
+ return loco::NodeShape{from_shape};
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+bool CircleShapeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return CircleDialect::get() == d;
+}
+
+bool CircleShapeInferenceRule::infer(const loco::Node *node, loco::NodeShape &shape) const
+{
+ assert(node->dialect() == CircleDialect::get());
+ assert(dynamic_cast<const CircleNode *>(node) != nullptr);
+
+ ShapeInferenceAlgorithm alg;
+ shape = dynamic_cast<const CircleNode *>(node)->accept(&alg);
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp
new file mode 100644
index 000000000..0374251a0
--- /dev/null
+++ b/compiler/luci/service/src/CircleShapeInferenceRule.test.cpp
@@ -0,0 +1,282 @@
+/*
+ * 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 "TestGraph.h"
+#include "luci/Service/CircleShapeInferenceRule.h"
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/CanonicalShapeInferenceRule.h>
+#include <loco/Service/MultiDialectShapeInferenceRule.h>
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+namespace
+{
+
+bool shape_pass(loco::Graph *g)
+{
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ luci::CircleShapeInferenceRule circle_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &circle_rule);
+
+ return loco::apply(&rules).to(g);
+}
+
+} // namespace
+
+TEST(CircleShapeInferenceRuleTest, minimal_with_CircleRelu)
+{
+ // Create a simple network
+ luci::test::TestGraph graph;
+ auto tfl_node = graph.append<luci::CircleRelu>(graph.pull);
+ graph.complete(tfl_node);
+
+ // set shape
+ {
+ graph.pull->rank(2);
+ graph.pull->dim(0) = 3;
+ graph.pull->dim(1) = 4;
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ luci::CircleShapeInferenceRule tfl_rule;
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(shape.rank(), 2);
+ ASSERT_EQ(shape.dim(0), 3);
+ ASSERT_EQ(shape.dim(1), 4);
+ }
+}
+
+// based on the case shown in
+// https://www.corvil.com/kb/what-is-the-difference-between-same-and-valid-padding-in-tf-nn-max-pool-of-tensorflow
+TEST(CircleShapeInferenceRuleTest, avgpool2d_valid)
+{
+ luci::test::TestGraph graph;
+ auto tfl_node = graph.append<luci::CircleAveragePool2D>(graph.pull);
+ graph.complete();
+
+ auto pull = graph.pull;
+ {
+ pull->shape({1, 4, 3, 1});
+ }
+ // setting CircleAveragePool2D
+ {
+ tfl_node->filter()->h(2);
+ tfl_node->filter()->w(2);
+ tfl_node->stride()->h(2);
+ tfl_node->stride()->w(2);
+ tfl_node->fusedActivationFunction(luci::FusedActFunc::NONE);
+ tfl_node->padding(luci::Padding::VALID);
+ }
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ luci::CircleShapeInferenceRule tfl_rule;
+ loco::CanonicalShapeInferenceRule canonical_rule;
+ loco::MultiDialectShapeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canonical_rule)
+ .bind(luci::CircleDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(shape.rank(), 4);
+ ASSERT_EQ(shape.dim(0).value(), 1);
+ ASSERT_EQ(shape.dim(1).value(), 2);
+ ASSERT_EQ(shape.dim(2).value(), 1);
+ ASSERT_EQ(shape.dim(3).value(), 1);
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, avgpool2d_same)
+{
+ luci::test::TestGraph graph;
+ auto tfl_node = graph.append<luci::CircleAveragePool2D>(graph.pull);
+ graph.complete();
+
+ auto pull = graph.pull;
+ {
+ pull->shape({1, 4, 3, 1});
+ }
+
+ // setting CircleAveragePool2D
+ {
+ tfl_node->filter()->h(2);
+ tfl_node->filter()->w(2);
+ tfl_node->stride()->h(2);
+ tfl_node->stride()->w(2);
+ tfl_node->fusedActivationFunction(luci::FusedActFunc::NONE);
+ tfl_node->padding(luci::Padding::SAME);
+ }
+
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ shape_pass(graph.g.get());
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(shape.rank(), 4);
+ ASSERT_EQ(shape.dim(0).value(), 1);
+ ASSERT_EQ(shape.dim(1).value(), 2);
+ ASSERT_EQ(shape.dim(2).value(), 2);
+ ASSERT_EQ(shape.dim(3).value(), 1);
+ }
+}
+
+/**
+ * @note Function to test: Shape inference of two different input shapes
+ *
+ * Rank expansion to higher input side
+ * x(2,1,5) + y(3,5) --> x(2,1,5) + y(1,3,5)
+ * Do output shape inference like numpy
+ * x(2,1,5) + y(1,3,5) --> output(2,3,5)
+ * For each axis, dim value should be same OR one of them should be 1
+ */
+TEST(CircleShapeInferenceRuleTest, TFAdd_shapeinf_different)
+{
+ auto g = loco::make_graph();
+
+ auto x_node = g->nodes()->create<loco::Pull>();
+ {
+ x_node->rank(3);
+ x_node->dim(0) = 2;
+ x_node->dim(1) = 1;
+ x_node->dim(2) = 5;
+ }
+ auto y_node = g->nodes()->create<loco::Pull>();
+ {
+ y_node->rank(2);
+ y_node->dim(0) = 3;
+ y_node->dim(1) = 5;
+ }
+ auto tfl_node = g->nodes()->create<luci::CircleAdd>();
+ {
+ tfl_node->x(x_node);
+ tfl_node->y(y_node);
+ }
+ auto push_node = g->nodes()->create<loco::Push>();
+ {
+ push_node->from(tfl_node);
+ }
+
+ auto x_input = g->inputs()->create();
+ {
+ x_input->name("x");
+ loco::link(x_input, x_node);
+ }
+ auto y_input = g->inputs()->create();
+ {
+ y_input->name("y");
+ loco::link(y_input, y_node);
+ }
+ auto output = g->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push_node);
+ }
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(tfl_node));
+
+ // shape inference
+ while (shape_pass(g.get()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(tfl_node));
+ ASSERT_EQ(loco::shape_get(tfl_node).domain(), loco::Domain::Tensor);
+
+ auto shape = loco::shape_get(tfl_node).as<loco::TensorShape>();
+ ASSERT_EQ(shape.rank(), 3);
+ ASSERT_EQ(shape.dim(0), 2);
+ ASSERT_EQ(shape.dim(1), 3);
+ ASSERT_EQ(shape.dim(2), 5);
+ }
+}
+
+TEST(CircleShapeInferenceRuleTest, CircleTranspose_simple)
+{
+ luci::test::ExampleGraph<luci::test::ExampleGraphType::CircleTranspose> g;
+
+ g.pull->rank(3);
+ g.pull->dim(0) = 3;
+ g.pull->dim(1) = 8;
+ g.pull->dim(2) = 1;
+
+ g.const_perm->dtype(loco::DataType::S32);
+ g.const_perm->rank(1);
+ g.const_perm->dim(0) = 3;
+ g.const_perm->size<loco::DataType::S32>(3);
+ g.const_perm->at<loco::DataType::S32>(0) = 1;
+ g.const_perm->at<loco::DataType::S32>(1) = 2;
+ g.const_perm->at<loco::DataType::S32>(2) = 0;
+
+ // pre-check
+ ASSERT_FALSE(loco::shape_known(g.transpose_node));
+
+ // shape inference
+ while (shape_pass(g.graph()) == true)
+ ;
+
+ // Verify
+ {
+ ASSERT_TRUE(loco::shape_known(g.transpose_node));
+
+ auto shape = loco::shape_get(g.transpose_node).as<loco::TensorShape>();
+ ASSERT_EQ(shape.rank(), 3);
+ ASSERT_EQ(shape.dim(0), 8);
+ ASSERT_EQ(shape.dim(1), 1);
+ ASSERT_EQ(shape.dim(2), 3);
+ }
+}
diff --git a/compiler/luci/service/src/CircleTypeInference.cpp b/compiler/luci/service/src/CircleTypeInference.cpp
new file mode 100644
index 000000000..669906159
--- /dev/null
+++ b/compiler/luci/service/src/CircleTypeInference.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 "luci/Service/CircleTypeInference.h"
+#include "luci/Service/CircleTypeInferenceRule.h"
+
+#include <luci/IR/CircleDialect.h>
+
+#include <loco/IR/CanonicalNode.h>
+#include <loco/IR/CanonicalNodeVisitor.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+#include <mio/circle/schema_generated.h>
+#include <oops/InternalExn.h>
+
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+
+namespace
+{
+
+circle::TensorType translateLocoTypeToCircle(loco::DataType dtype)
+{
+ switch (dtype)
+ {
+ case loco::DataType::U8:
+ return circle::TensorType_UINT8;
+ // case loco::DataType::U16: unsupported
+ // case loco::DataType::U32: unsupported
+ // case loco::DataType::U64: unsupported
+ case loco::DataType::S8:
+ return circle::TensorType_INT8;
+ case loco::DataType::S16:
+ return circle::TensorType_INT16;
+ case loco::DataType::S32:
+ return circle::TensorType_INT32;
+ case loco::DataType::S64:
+ return circle::TensorType_INT64;
+ case loco::DataType::FLOAT16:
+ return circle::TensorType_FLOAT16;
+ case loco::DataType::FLOAT32:
+ return circle::TensorType_FLOAT32;
+ // case loco::DataType::FLOAT64: unsupported
+ case loco::DataType::BOOL:
+ return circle::TensorType_BOOL;
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Invalid loco dtype", oops::to_uint32(dtype));
+}
+
+} // namespace
+
+namespace luci
+{
+
+circle::TensorType TypeInference::get(loco::Node *node)
+{
+ assert(loco::dtype_known(node));
+ return translateLocoTypeToCircle(loco::dtype_get(node));
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.cpp
new file mode 100644
index 000000000..21a28c1b6
--- /dev/null
+++ b/compiler/luci/service/src/CircleTypeInferenceRule.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 "luci/Service/CircleTypeInferenceRule.h"
+
+#include <luci/IR/CircleDialect.h>
+#include <luci/IR/CircleNodeVisitor.h>
+#include <luci/IR/CircleNodes.h>
+
+#include <cassert>
+
+namespace
+{
+
+struct TypeInferenceAlgorithm final : public luci::CircleNodeVisitor<loco::DataType>
+{
+ // TODO Given a tensor x of complex numbers, Abs operation returns a tensor of type float32 or
+ // float64.
+ loco::DataType visit(const luci::CircleAbs *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleAdd *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleArgMax *node) final { return node->output_type(); }
+
+ loco::DataType visit(const luci::CircleAveragePool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleBatchToSpaceND *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleConcatenation *node) final
+ {
+ // TODO Support when CircleConcatenation has 0 input
+ assert(node->numValues() > 0);
+
+ for (uint32_t i = 1; i < node->numValues(); ++i)
+ assert(loco::dtype_get(node->values(i - 1)) == loco::dtype_get(node->values(i)));
+
+ return loco::dtype_get(node->values(0));
+ }
+
+ loco::DataType visit(const luci::CircleConst *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleConv2D *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleCos *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleDepthwiseConv2D *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleDiv *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleEqual *) final { return loco::DataType::BOOL; }
+
+ loco::DataType visit(const luci::CircleExp *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleFullyConnected *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CircleLogicalNot *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleLogicalOr *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleMaximum *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleMaxPool2D *node) final
+ {
+ return loco::dtype_get(node->value());
+ }
+
+ loco::DataType visit(const luci::CircleMean *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ loco::DataType visit(const luci::CirclePack *node) final
+ {
+ // Only support CirclePack with one or more inputs
+ assert(node->values_count() > 0);
+
+ auto first_value_type = loco::dtype_get(node->values(0));
+ for (uint32_t i = 1; i < node->values_count(); ++i)
+ assert(first_value_type == loco::dtype_get(node->values(i)));
+
+ return first_value_type;
+ }
+
+ loco::DataType visit(const luci::CirclePad *node) final { return loco::dtype_get(node->input()); }
+
+ loco::DataType visit(const luci::CircleMul *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleRelu *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleRelu6 *node) final
+ {
+ return loco::dtype_get(node->features());
+ }
+
+ loco::DataType visit(const luci::CircleReshape *node) final
+ {
+ return loco::dtype_get(node->tensor());
+ }
+
+ loco::DataType visit(const luci::CircleRsqrt *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleSoftmax *node) final
+ {
+ return loco::dtype_get(node->logits());
+ }
+
+ loco::DataType visit(const luci::CircleSqrt *node) final { return loco::dtype_get(node->x()); }
+
+ loco::DataType visit(const luci::CircleSquaredDifference *node) final
+ {
+ return loco::dtype_get(node->x());
+ }
+
+ loco::DataType visit(const luci::CircleSub *node) final { return loco::dtype_get(node->x()); }
+
+ // TODO CircleTanh
+
+ loco::DataType visit(const luci::CircleTranspose *node) final
+ {
+ return loco::dtype_get(node->a());
+ }
+
+ loco::DataType visit(const luci::CircleTransposeConv *node) final
+ {
+ return loco::dtype_get(node->outBackprop());
+ }
+
+ // Circle Only
+ loco::DataType visit(const luci::CircleInstanceNorm *node) final
+ {
+ return loco::dtype_get(node->input());
+ }
+
+ // Virtual
+ loco::DataType visit(const luci::CircleInput *node) final { return node->dtype(); }
+
+ loco::DataType visit(const luci::CircleOutput *node) final
+ {
+ return loco::dtype_get(node->from());
+ }
+};
+
+} // namespace
+
+namespace luci
+{
+
+bool CircleTypeInferenceRule::recognize(const loco::Dialect *d) const
+{
+ return CircleDialect::get() == d;
+}
+
+bool CircleTypeInferenceRule::infer(const loco::Node *node, loco::DataType &dtype) const
+{
+ assert(node->dialect() == CircleDialect::get());
+
+ TypeInferenceAlgorithm alg;
+
+ dtype = dynamic_cast<const CircleNode *>(node)->accept(&alg);
+ assert(dtype != loco::DataType::Unknown);
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp
new file mode 100644
index 000000000..29f45173e
--- /dev/null
+++ b/compiler/luci/service/src/CircleTypeInferenceRule.test.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "TestGraph.h"
+#include <luci/Service/CircleTypeInferenceRule.h>
+
+#include <luci/IR/CircleNodes.h>
+#include <luci/IR/CircleDialect.h>
+
+#include <loco.h>
+#include <loco/IR/CanonicalDialect.h>
+#include <loco/Service/TypeInference.h>
+
+#include <gtest/gtest.h>
+
+#include <memory>
+
+TEST(CircleTypeInferenceRuleTest, minimal_with_CircleRelu)
+{
+ // Create a simple network
+ luci::test::TestGraph graph;
+ auto tfl_node = graph.append<luci::CircleRelu>(graph.pull);
+ graph.complete(tfl_node);
+
+ graph.pull->dtype(loco::DataType::S32);
+
+ // pre-check
+ ASSERT_FALSE(loco::dtype_known(tfl_node));
+
+ // type inference
+ luci::CircleTypeInferenceRule tfl_rule;
+ loco::CanonicalTypeInferenceRule canon_rule;
+ loco::MultiDialectTypeInferenceRule rules;
+
+ rules.bind(loco::CanonicalDialect::get(), &canon_rule);
+ rules.bind(luci::CircleDialect::get(), &tfl_rule);
+
+ loco::apply(&rules).to(graph.g.get());
+
+ // Verify
+ ASSERT_TRUE(loco::dtype_known(tfl_node));
+ auto type = loco::dtype_get(tfl_node);
+ ASSERT_EQ(type, loco::DataType::S32);
+}
diff --git a/compiler/luci/service/src/GraphBlock.h b/compiler/luci/service/src/GraphBlock.h
new file mode 100644
index 000000000..2a455888a
--- /dev/null
+++ b/compiler/luci/service/src/GraphBlock.h
@@ -0,0 +1,201 @@
+/*
+ * 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 __GRAPH_BLOCK_H__
+#define __GRAPH_BLOCK_H__
+
+#include <loco.h>
+#include <loco/Service/ShapeInference.h>
+
+#include <oops/InternalExn.h>
+
+#include <functional>
+
+// TODO Change all Canonical nodes to Circle nodes
+
+namespace luci
+{
+
+/// @brief feature layout of TFlite/Circle file
+enum class FeatureLayout
+{
+ NHWC,
+};
+
+/// @brief Creates a loco::FeatureEncode with T layout (NHWC for tflite) and add it to graph.
+template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode);
+
+/// @brief Creates a loco::FeatureDecode with T layout (NHWC for tflite) and add it to graph.
+template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode);
+
+enum class FilterLayout
+{
+ OHWI, // a.k.a., NHWC, Tensorflow Lite uses this layout for filter
+ HWIO, // a.k.a., HWCN, Tensorflow uses this layout for filter
+};
+
+/// @brief Create a loco::FilterEncode of given layout
+template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode);
+
+/// @brief Create a loco::FilterDecode of given layout
+template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode);
+
+enum class DepthwiseFilterLayout
+{
+ HWCM,
+};
+
+/// @brief Create a loco::DepthwiseFilterDecode of given layout
+template <DepthwiseFilterLayout T>
+loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode);
+
+enum class MatrixLayout
+{
+ HW,
+ WH
+};
+
+/// @brief Create a loco::MatrixEncode of given layout
+template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode);
+
+/// @brief Create a loco::MatrixDecode of given layout
+template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode);
+
+} // luci
+
+//
+// DomainConverter
+//
+
+/**
+ * Some canonical nodes can have input of various loco::Domain, e.g., loco::Domain::Tensor,
+ * loco::Domain::Feature, etc. However, TFL node accepts only loco::Domain::Tensor.
+ * So, When converting such canonical node to TFL node and input(s) of a canonical node are not
+ * loco::Domain::Tensor, additional nodes need to be inserted.
+ *
+ * The following two classes helps this insertion.
+ *
+ * For example, in case of loco::Relu conversion,
+ *
+ * Before:
+ *
+ * A (output: feature) -- loco::ReLU --- B (input:feature)
+ *
+ * After:
+ *
+ * A -- loco::FeatureDecode -- locoex::TFLRelu -- loco::FeatureEncode --- B
+ *
+ * loco::ReLU (dead node)
+ */
+
+namespace luci
+{
+
+/**
+ * @brief Handles input(s) while converting a canonical node to TFL node(s).
+ * This class informs DomainConverter how to handle inputs of a specific canonical node.
+ */
+template <class CanonicalT, class TFLT> class InputHandler
+{
+public:
+ /**
+ * @brief Assign origin's inputs to replacer's inputs.
+ * (This is called when origin belongs in Tensor domain.)
+ */
+ virtual void handover(CanonicalT *origin, TFLT *replacer) = 0;
+
+ /**
+ * @brief Returns the list of inputs that needs to have FeatureDecode as its input.
+ * (This is called when origin belongs in Feature domain.)
+ */
+ virtual std::vector<loco::Node *> getInputsToConvert(CanonicalT *origin) = 0;
+
+ /// @brief Set the inputs of replacer to new_inputs
+ virtual void set(TFLT *replacer, std::vector<loco::Node *> &new_inputs) = 0;
+
+ /// @brief Set the inputs to nullptr
+ virtual void nullify(CanonicalT *origin) = 0;
+};
+
+/**
+ * @brief Class to handle domain conversion while converting a canonical node to TFL node(s)
+ */
+template <class CanonicalT, class TFLT> class DomainConverter
+{
+public:
+ template <FeatureLayout FeatureLayoutT>
+ TFLT *convert(CanonicalT *origin, InputHandler<CanonicalT, TFLT> &input_handler);
+};
+
+/**
+ * @brief Performs domain conversion
+ *
+ * 1. if origin belong to loco::Domain::Tensor, and replace origin to a TFL node.
+ * 2. if origin belong to loco::Domain::Feature, insert loco::FeatureDecode for input(s) and
+ * insert loco::FeatureEncode for output. Then replace origin to a TFL node.
+ *
+ * @return new TFL node; nullptr if shape of origin cannot be known
+ */
+template <class CanonicalT, class TFLT>
+template <FeatureLayout FeatureLayoutT>
+TFLT *DomainConverter<CanonicalT, TFLT>::convert(CanonicalT *origin,
+ InputHandler<CanonicalT, TFLT> &input_handler)
+{
+ static_assert(FeatureLayoutT == FeatureLayout::NHWC, "Feature layout should be NHWC");
+
+ if (!loco::shape_known(origin))
+ {
+ return nullptr;
+ }
+
+ auto tfl_node = origin->graph()->nodes()->template create<TFLT>();
+
+ // when the input is Tensor, just replace canonical node to TFL node.
+ if (loco::shape_get(origin).domain() == loco::Domain::Tensor)
+ {
+ input_handler.handover(origin, tfl_node);
+
+ loco::replace(origin).with(tfl_node);
+ input_handler.nullify(origin);
+
+ return tfl_node;
+ }
+ else if (loco::shape_get(origin).domain() == loco::Domain::Feature)
+ {
+ std::vector<loco::Node *> feature_decodes;
+
+ for (auto input : input_handler.getInputsToConvert(origin))
+ {
+ auto dec = make_feature_decode<FeatureLayoutT>(input);
+ feature_decodes.emplace_back(dec);
+ }
+
+ input_handler.set(tfl_node, feature_decodes);
+
+ auto enc = make_feature_encode<FeatureLayoutT>(tfl_node);
+
+ loco::replace(origin).with(enc);
+ input_handler.nullify(origin);
+
+ return tfl_node;
+ }
+ else
+ INTERNAL_EXN_V("Unsupported loco::Domain", oops::to_uint32(loco::shape_get(origin).domain()));
+}
+
+} // namespace luci
+
+#endif //__GRAPH_BLOCK_H__
diff --git a/compiler/luci/service/src/GraphBlock.test.cpp b/compiler/luci/service/src/GraphBlock.test.cpp
new file mode 100644
index 000000000..1da8c18fa
--- /dev/null
+++ b/compiler/luci/service/src/GraphBlock.test.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "GraphBlock.h"
+
+#include "Check.h"
+
+#include <loco.h>
+
+#include <memory>
+
+// TODO Change all Canonical nodes to Circle nodes
+
+namespace
+{
+
+template <luci::FeatureLayout T> loco::Permutation<loco::Domain::Feature> perm();
+
+template <> loco::Permutation<loco::Domain::Feature> perm<luci::FeatureLayout::NHWC>()
+{
+ // Make NHWC permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Feature> NHWC;
+
+ NHWC.axis(loco::FeatureAxis::Count) = 0;
+ NHWC.axis(loco::FeatureAxis::Height) = 1;
+ NHWC.axis(loco::FeatureAxis::Width) = 2;
+ NHWC.axis(loco::FeatureAxis::Depth) = 3;
+
+ return NHWC;
+}
+
+template <luci::FilterLayout T> loco::Permutation<loco::Domain::Filter> perm();
+
+template <> loco::Permutation<loco::Domain::Filter> perm<luci::FilterLayout::HWIO>()
+{
+ loco::Permutation<loco::Domain::Filter> HWIO; // a.k.a., HWCN
+
+ HWIO.axis(loco::FilterAxis::Height) = 0;
+ HWIO.axis(loco::FilterAxis::Width) = 1;
+ HWIO.axis(loco::FilterAxis::Depth) = 2;
+ HWIO.axis(loco::FilterAxis::Count) = 3;
+
+ return HWIO;
+}
+
+template <> loco::Permutation<loco::Domain::Filter> perm<luci::FilterLayout::OHWI>()
+{
+
+ // Make NHWC permutation for encoder and decoder
+ loco::Permutation<loco::Domain::Filter> OHWI; // a.k.a., NHWC
+
+ OHWI.axis(loco::FilterAxis::Count) = 0;
+ OHWI.axis(loco::FilterAxis::Height) = 1;
+ OHWI.axis(loco::FilterAxis::Width) = 2;
+ OHWI.axis(loco::FilterAxis::Depth) = 3;
+
+ return OHWI;
+}
+
+template <luci::DepthwiseFilterLayout T> loco::Permutation<loco::Domain::DepthwiseFilter> perm();
+
+template <>
+loco::Permutation<loco::Domain::DepthwiseFilter> perm<luci::DepthwiseFilterLayout::HWCM>()
+{
+ loco::Permutation<loco::Domain::DepthwiseFilter> HWCM;
+
+ HWCM.axis(loco::DepthwiseFilterAxis::Height) = 0;
+ HWCM.axis(loco::DepthwiseFilterAxis::Width) = 1;
+ HWCM.axis(loco::DepthwiseFilterAxis::Depth) = 2;
+ HWCM.axis(loco::DepthwiseFilterAxis::Multiplier) = 3;
+
+ return HWCM;
+}
+
+template <luci::MatrixLayout T> loco::Permutation<loco::Domain::Matrix> perm();
+
+template <> loco::Permutation<loco::Domain::Matrix> perm<luci::MatrixLayout::HW>()
+{
+ loco::Permutation<loco::Domain::Matrix> HW;
+
+ HW.axis(loco::MatrixAxis::Height) = 0;
+ HW.axis(loco::MatrixAxis::Width) = 1;
+
+ return HW;
+}
+
+template <> loco::Permutation<loco::Domain::Matrix> perm<luci::MatrixLayout::WH>()
+{
+ loco::Permutation<loco::Domain::Matrix> WH;
+
+ WH.axis(loco::MatrixAxis::Height) = 1;
+ WH.axis(loco::MatrixAxis::Width) = 0;
+
+ return WH;
+}
+
+} // namespace
+
+namespace luci
+{
+
+template <FeatureLayout T> loco::FeatureEncode *make_feature_encode(loco::Node *input_for_encode)
+{
+ LUCI_ASSERT(input_for_encode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Feature>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::FeatureEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+template <FeatureLayout T> loco::FeatureDecode *make_feature_decode(loco::Node *input_for_decode)
+{
+ LUCI_ASSERT(input_for_decode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Feature>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::FeatureDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <FilterLayout T> loco::FilterEncode *make_filter_encode(loco::Node *input_for_encode)
+{
+ LUCI_ASSERT(input_for_encode != nullptr, "filter should not be nullptr");
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Filter>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::FilterEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+template <FilterLayout T> loco::FilterDecode *make_filter_decode(loco::Node *input_for_decode)
+{
+ LUCI_ASSERT(input_for_decode != nullptr, "filter should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Filter>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::FilterDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <DepthwiseFilterLayout T>
+loco::DepthwiseFilterDecode *make_dw_filter_decode(loco::Node *input_for_decode)
+{
+ LUCI_ASSERT(input_for_decode != nullptr, "filter should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::DepthwiseFilter>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::DepthwiseFilterDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+template <MatrixLayout T> loco::MatrixEncode *make_matrix_encode(loco::Node *input_for_encode)
+{
+ LUCI_ASSERT(input_for_encode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_encode->graph();
+
+ auto encoder = std::make_unique<loco::PermutingEncoder<loco::Domain::Matrix>>();
+
+ encoder->perm(perm<T>());
+
+ auto enc = g->nodes()->create<loco::MatrixEncode>();
+ enc->input(input_for_encode);
+ enc->encoder(std::move(encoder));
+
+ return enc;
+}
+
+template <MatrixLayout T> loco::MatrixDecode *make_matrix_decode(loco::Node *input_for_decode)
+{
+ LUCI_ASSERT(input_for_decode != nullptr, "input should not be nullptr");
+ loco::Graph *g = input_for_decode->graph();
+
+ auto decoder = std::make_unique<loco::PermutingDecoder<loco::Domain::Matrix>>();
+
+ decoder->perm(perm<T>());
+
+ auto dec = g->nodes()->create<loco::MatrixDecode>();
+ dec->input(input_for_decode);
+ dec->decoder(std::move(decoder));
+
+ return dec;
+}
+
+// template instantiation
+template loco::FeatureEncode *
+make_feature_encode<FeatureLayout::NHWC>(loco::Node *input_for_encode);
+
+template loco::FeatureDecode *
+make_feature_decode<FeatureLayout::NHWC>(loco::Node *input_for_encode);
+
+template loco::FilterEncode *make_filter_encode<FilterLayout::HWIO>(loco::Node *input_for_encode);
+template loco::FilterDecode *make_filter_decode<FilterLayout::OHWI>(loco::Node *input_for_decode);
+
+template loco::DepthwiseFilterDecode *
+make_dw_filter_decode<DepthwiseFilterLayout::HWCM>(loco::Node *input_for_decode);
+
+template loco::MatrixEncode *make_matrix_encode<MatrixLayout::HW>(loco::Node *input_for_encode);
+template loco::MatrixEncode *make_matrix_encode<MatrixLayout::WH>(loco::Node *input_for_encode);
+template loco::MatrixDecode *make_matrix_decode<MatrixLayout::HW>(loco::Node *input_for_decode);
+template loco::MatrixDecode *make_matrix_decode<MatrixLayout::WH>(loco::Node *input_for_decode);
+
+} // namespace luci
diff --git a/compiler/luci/service/src/ShapeDescription.cpp b/compiler/luci/service/src/ShapeDescription.cpp
new file mode 100644
index 000000000..cbc302f70
--- /dev/null
+++ b/compiler/luci/service/src/ShapeDescription.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 "luci/Service/ShapeDescription.h"
+
+#include <oops/InternalExn.h>
+
+#include <cassert>
+
+namespace luci
+{
+
+ShapeDescription to_shape_description(const loco::TensorShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(shape.rank());
+ for (uint32_t axis = 0; axis < shape.rank(); ++axis)
+ {
+ // All the dimensions SHOULD be known
+ assert(shape.dim(axis).known());
+ res._dims.at(axis) = shape.dim(axis).value();
+ }
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::FeatureShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a feature map as a NHWC tensor
+ res._dims.resize(4);
+ res._dims.at(0) = shape.count().value();
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::FilterShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a convolution filter as a NHWC tensor
+ res._dims.resize(4);
+ res._dims.at(0) = shape.count().value();
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::DepthwiseFilterShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ // T/F Lite encodes a depthwise convolution filter as a [1, H, W, C*M] tensor
+ res._dims.resize(4);
+ res._dims.at(0) = 1;
+ res._dims.at(1) = shape.height().value();
+ res._dims.at(2) = shape.width().value();
+ res._dims.at(3) = shape.depth().value() * shape.multiplier().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::BiasShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(1);
+ res._dims.at(0) = shape.length().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::MatrixShape &shape)
+{
+ ShapeDescription res;
+
+ res._rank_known = true;
+
+ res._dims.resize(2);
+ res._dims.at(0) = shape.height().value();
+ res._dims.at(1) = shape.width().value();
+
+ return res;
+}
+
+ShapeDescription to_shape_description(const loco::NodeShape &shape)
+{
+ switch (shape.domain())
+ {
+ case loco::Domain::Tensor:
+ return to_shape_description(shape.as<loco::TensorShape>());
+ case loco::Domain::Feature:
+ return to_shape_description(shape.as<loco::FeatureShape>());
+ case loco::Domain::Filter:
+ return to_shape_description(shape.as<loco::FilterShape>());
+ case loco::Domain::DepthwiseFilter:
+ return to_shape_description(shape.as<loco::DepthwiseFilterShape>());
+ case loco::Domain::Bias:
+ return to_shape_description(shape.as<loco::BiasShape>());
+ case loco::Domain::Matrix:
+ return to_shape_description(shape.as<loco::MatrixShape>());
+ default:
+ break;
+ }
+
+ INTERNAL_EXN_V("Unsupported loco domain", oops::to_uint32(shape.domain()));
+}
+
+} // namespace luci
diff --git a/compiler/luci/service/src/TestGraph.h b/compiler/luci/service/src/TestGraph.h
new file mode 100644
index 000000000..73562040f
--- /dev/null
+++ b/compiler/luci/service/src/TestGraph.h
@@ -0,0 +1,315 @@
+/*
+ * 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 __TEST_GRAPH_H__
+#define __TEST_GRAPH_H__
+
+#include <luci/IR/CircleNodes.h>
+#include "GraphBlock.h"
+
+#include <loco.h>
+
+#include <cassert>
+#include <memory>
+
+// TODO Change all Canonical nodes to Circle nodes
+
+namespace luci
+{
+namespace test
+{
+
+class TestGraph
+{
+public:
+ std::unique_ptr<loco::Graph> g;
+ loco::Pull *pull;
+ loco::Push *push;
+
+ TestGraph() // creates Pull and Push
+ {
+ g = loco::make_graph();
+
+ pull = g->nodes()->create<loco::Pull>();
+
+ push = g->nodes()->create<loco::Push>();
+
+ auto input = g->inputs()->create();
+ {
+ input->name("input");
+ loco::link(input, pull);
+ }
+ auto output = g->outputs()->create();
+ {
+ output->name("output");
+ loco::link(output, push);
+ }
+
+ _next_input = pull;
+ }
+
+ loco::Graph *graph() { return g.get(); }
+
+ /// @brief Creates node with NO arg and appends it to graph
+ template <class T> T *append()
+ {
+ auto node = g->nodes()->create<T>();
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=1) with arg1 as an input and appends it to graph
+ template <class T> T *append(loco::Node *arg1)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1);
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=2) with arg1, arg2 as inputs and appends it to graph
+ template <class T> T *append(loco::Node *arg1, loco::Node *arg2)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1, arg2);
+ _next_input = node;
+
+ return node;
+ }
+
+ /// @brief Creates op T (arity=3) with arg1, arg2, arg3 as inputs and appends it to graph
+ template <class T> T *append(loco::Node *arg1, loco::Node *arg2, loco::Node *arg3)
+ {
+ auto node = g->nodes()->create<T>();
+ setInput(node, arg1, arg2, arg3);
+ _next_input = node;
+
+ return node;
+ }
+
+ // push will get the last appended node
+ void complete() { push->from(_next_input); }
+
+ void complete(loco::Node *last_node) { push->from(last_node); }
+
+private:
+ // arity 1
+ void setInput(loco::Node *node, loco::Node *) { assert(false && "NYI"); };
+
+ void setInput(loco::AvgPool2D *node, loco::Node *input) { node->ifm(input); }
+ void setInput(loco::BiasDecode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::BiasEncode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::FeatureDecode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::FeatureEncode *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::MaxPool2D *node, loco::Node *input) { node->ifm(input); }
+ void setInput(loco::Push *node, loco::Node *input) { node->from(input); };
+ void setInput(loco::ReLU *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::ReLU6 *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::Tanh *node, loco::Node *input) { node->input(input); };
+ void setInput(loco::TensorTranspose *node, loco::Node *input) { node->input(input); };
+
+ void setInput(luci::CircleAveragePool2D *node, loco::Node *input) { node->value(input); };
+ void setInput(luci::CircleMaxPool2D *node, loco::Node *input) { node->value(input); };
+ void setInput(luci::CircleRelu *node, loco::Node *input) { node->features(input); };
+ void setInput(luci::CircleRelu6 *node, loco::Node *input) { node->features(input); };
+
+ // arity 2
+ void setInput(loco::Node *node, loco::Node *, loco::Node *) { assert(false && "NYI"); };
+
+ void setInput(loco::Conv2D *node, loco::Node *input, loco::Node *filter)
+ {
+ node->ifm(input);
+ node->ker(filter);
+ }
+
+ void setInput(loco::EltwiseAdd *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->lhs(arg1);
+ node->rhs(arg2);
+ };
+
+ void setInput(loco::FeatureBiasAdd *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->value(arg1);
+ node->bias(arg2);
+ };
+
+ void setInput(luci::CircleAdd *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->x(arg1);
+ node->y(arg2);
+ };
+
+ void setInput(luci::CircleMul *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->x(arg1);
+ node->y(arg2);
+ };
+
+ void setInput(luci::CircleSub *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->x(arg1);
+ node->y(arg2);
+ };
+
+ void setInput(luci::CircleTranspose *node, loco::Node *arg1, loco::Node *arg2)
+ {
+ node->a(arg1);
+ node->perm(arg2);
+ };
+
+ // arity 3
+ void setInput(loco::Node *node, loco::Node *, loco::Node *, loco::Node *)
+ {
+ assert(false && "NYI");
+ };
+
+ void setInput(luci::CircleConv2D *node, loco::Node *input, loco::Node *filter, loco::Node *bias)
+ {
+ node->input(input);
+ node->filter(filter);
+ node->bias(bias);
+ }
+
+private:
+ loco::Node *_next_input;
+};
+
+enum class ExampleGraphType
+{
+ FeatureBiasAdd,
+ ConstGen_ReLU,
+ FilterEncode_FilterDecode,
+ Transpose,
+
+ CircleTranspose,
+};
+
+template <ExampleGraphType T> class ExampleGraph;
+
+/**
+ * @brief Class to create the following:
+ *
+ * Pull - FeatureEncoder - FeatureBiasAdd - FeatureDecode - Push
+ * |
+ * ConstGen - BiasEncode --+
+ */
+template <> class ExampleGraph<ExampleGraphType::FeatureBiasAdd> : public TestGraph
+{
+public:
+ loco::FeatureEncode *fea_enc = nullptr;
+ loco::ConstGen *constgen = nullptr;
+ loco::BiasEncode *bias_enc = nullptr;
+ loco::FeatureBiasAdd *fea_bias_add = nullptr;
+ loco::FeatureDecode *fea_dec = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ fea_enc = luci::make_feature_encode<luci::FeatureLayout::NHWC>(pull);
+ constgen = append<loco::ConstGen>();
+ bias_enc = append<loco::BiasEncode>(constgen);
+ fea_bias_add = append<loco::FeatureBiasAdd>(fea_enc, bias_enc);
+ fea_dec = luci::make_feature_decode<luci::FeatureLayout::NHWC>(fea_bias_add);
+ complete(fea_dec);
+ }
+};
+
+/**
+ * @brief Class to creates the following:
+ *
+ * ConstGen -- ReLU -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::ConstGen_ReLU> : public TestGraph
+{
+public:
+ loco::ConstGen *constgen = nullptr;
+ loco::ReLU *relu = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ constgen = append<loco::ConstGen>();
+ relu = append<loco::ReLU>(constgen);
+ complete(relu);
+ }
+};
+
+/**
+ * @brief Class to creates the following:
+ *
+ * Pull -- Transpose -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::Transpose> : public TestGraph
+{
+public:
+ loco::TensorTranspose *transpose = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ transpose = append<loco::TensorTranspose>(pull);
+ complete(transpose);
+ }
+};
+
+/**
+ * @brief Class to creates the following:
+ *
+ * Pull -- FilterEncode -- FilterDecode -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::FilterEncode_FilterDecode> : public TestGraph
+{
+public:
+ loco::FilterEncode *filterEncode = nullptr;
+ loco::FilterDecode *filterDecode = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ filterEncode = luci::make_filter_encode<luci::FilterLayout::HWIO>(pull); // from Tensorflow
+ filterDecode =
+ luci::make_filter_decode<luci::FilterLayout::OHWI>(filterEncode); // to Tensorflow Lite
+ complete(filterDecode);
+ }
+};
+
+/**
+ * @brief Class to create the following:
+ *
+ * Pull -- CircleTranspose -- Push
+ */
+template <> class ExampleGraph<ExampleGraphType::CircleTranspose> : public TestGraph
+{
+public:
+ loco::ConstGen *const_perm = nullptr;
+ luci::CircleTranspose *transpose_node = nullptr;
+
+public:
+ ExampleGraph()
+ {
+ const_perm = append<loco::ConstGen>();
+ transpose_node = append<luci::CircleTranspose>(pull, const_perm);
+ complete(transpose_node);
+ }
+};
+
+} // namespace test
+} // namespace luci
+
+#endif // __TEST_GRAPH_H__
diff --git a/compiler/luci/service/src/Validate.cpp b/compiler/luci/service/src/Validate.cpp
new file mode 100644
index 000000000..65b82c2b4
--- /dev/null
+++ b/compiler/luci/service/src/Validate.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 "luci/Service/Validate.h"
+
+#include <luci/IR/Nodes/CircleOutput.h>
+#include <luci/Log.h>
+
+#include <loco/IR/NodeShape.h>
+#include <loco/Service/ShapeInference.h>
+#include <loco/Service/TypeInference.h>
+
+#include <cassert>
+#include <vector>
+
+namespace
+{
+
+/**
+ * @brief returns a node that is CircleOutput with index is out_index in nodes
+ */
+luci::CircleOutput *find_node(std::vector<loco::Node *> nodes, loco::GraphOutputIndex out_index)
+{
+ for (auto node : nodes)
+ {
+ auto circle_output = dynamic_cast<luci::CircleOutput *>(node);
+ if (circle_output != nullptr)
+ {
+ if (circle_output->indexed() && circle_output->index() == out_index)
+ return circle_output;
+ }
+ }
+ return nullptr;
+}
+
+bool validate_shape_type(loco::Graph *g)
+{
+ LOGGER(l);
+
+ auto output_nodes = loco::output_nodes(g);
+
+ auto count = g->outputs()->size();
+ for (uint32_t out = 0; out < count; ++out)
+ {
+ auto graph_out = g->outputs()->at(out);
+ auto out_index = graph_out->index();
+
+ auto circle_output = find_node(output_nodes, out_index);
+ assert(circle_output != nullptr);
+ assert(circle_output->from() != nullptr);
+ auto circle_node = dynamic_cast<luci::CircleNode *>(circle_output->from());
+ assert(circle_node != nullptr);
+ assert(loco::shape_known(circle_node));
+
+ // check if output node shape is same as graph output shape
+ auto co_shape = loco::shape_get(circle_node);
+ auto go_tensor_shape = graph_out->shape();
+ assert(go_tensor_shape);
+ auto go_shape = loco::NodeShape(*go_tensor_shape);
+ if (!(co_shape == go_shape))
+ {
+ INFO(l) << "Shape for #" << out_index << " not same " << std::endl;
+ return false;
+ }
+
+ // check if data type match
+ assert(loco::dtype_known(circle_node));
+ if (graph_out->dtype() != loco::dtype_get(circle_node))
+ {
+ INFO(l) << "Type for #" << out_index << " not same " << std::endl;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace
+
+namespace luci
+{
+
+bool validate(loco::Graph *g)
+{
+ if (!loco::valid(g))
+ return false;
+
+ if (!validate_shape_type(g))
+ return false;
+
+ // TODO add more validation
+
+ return true;
+}
+
+} // namespace luci
diff --git a/compiler/luci/tester/CMakeLists.txt b/compiler/luci/tester/CMakeLists.txt
new file mode 100644
index 000000000..bcb47183e
--- /dev/null
+++ b/compiler/luci/tester/CMakeLists.txt
@@ -0,0 +1,22 @@
+set(SRCS_READ_TESTER
+ src/ReadTester.cpp
+ src/Model.cpp
+ )
+
+add_executable(luci_readtester "${SRCS_READ_TESTER}")
+target_link_libraries(luci_readtester PRIVATE luci_import)
+target_link_libraries(luci_readtester PRIVATE luci_service)
+target_link_libraries(luci_readtester PRIVATE luci_pass)
+target_link_libraries(luci_readtester PRIVATE oops)
+
+set(SRCS_WRITE_TESTER
+ src/WriteTester.cpp
+ src/Model.cpp
+ )
+
+add_executable(luci_writetester "${SRCS_WRITE_TESTER}")
+target_link_libraries(luci_writetester PRIVATE luci_import)
+target_link_libraries(luci_writetester PRIVATE luci_service)
+target_link_libraries(luci_writetester PRIVATE luci_pass)
+target_link_libraries(luci_writetester PRIVATE luci_export)
+target_link_libraries(luci_writetester PRIVATE oops)
diff --git a/compiler/luci/tester/src/Model.cpp b/compiler/luci/tester/src/Model.cpp
new file mode 100644
index 000000000..b02c19161
--- /dev/null
+++ b/compiler/luci/tester/src/Model.cpp
@@ -0,0 +1,62 @@
+#include "Model.h"
+
+#include <fstream>
+#include <vector>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+namespace
+{
+
+class FileModel final : public luci::Model
+{
+public:
+ explicit FileModel(const std::string &filename) : _filename(filename) {}
+
+public:
+ FileModel(const FileModel &) = delete;
+ FileModel(FileModel &&) = delete;
+
+public:
+ const ::circle::Model *model(void) override
+ {
+ std::ifstream file(_filename, std::ios::binary | std::ios::in);
+ if (!file.good())
+ return nullptr;
+
+ file.unsetf(std::ios::skipws);
+
+ std::streampos fileSize;
+ file.seekg(0, std::ios::end);
+ fileSize = file.tellg();
+ file.seekg(0, std::ios::beg);
+
+ // reserve capacity
+ _data.reserve(fileSize);
+
+ // read the data
+ file.read(_data.data(), fileSize);
+ if (file.fail())
+ return nullptr;
+
+ return ::circle::GetModel(_data.data());
+ }
+
+private:
+ const std::string _filename;
+ std::vector<char> _data;
+};
+
+} // namespace
+
+namespace luci
+{
+
+std::unique_ptr<Model> load_model(const std::string &path)
+{
+ return std::make_unique<FileModel>(path);
+}
+
+} // namespace luci
diff --git a/compiler/luci/tester/src/Model.h b/compiler/luci/tester/src/Model.h
new file mode 100644
index 000000000..e40faf33e
--- /dev/null
+++ b/compiler/luci/tester/src/Model.h
@@ -0,0 +1,27 @@
+#ifndef __TESTER_MODEL_H__
+#define __TESTER_MODEL_H__
+
+#include <mio/circle/schema_generated.h>
+
+#include <memory>
+
+namespace luci
+{
+
+struct Model
+{
+ virtual ~Model() = default;
+
+ virtual const ::circle::Model *model(void) = 0;
+};
+
+/**
+ * @brief Load Circle model (as a raw Model) from a given path
+ *
+ * @note May return a nullptr
+ */
+std::unique_ptr<Model> load_model(const std::string &path);
+
+} // namespace luci
+
+#endif // __TESTER_MODEL_H__
diff --git a/compiler/luci/tester/src/ReadTester.cpp b/compiler/luci/tester/src/ReadTester.cpp
new file mode 100644
index 000000000..c105d6ce3
--- /dev/null
+++ b/compiler/luci/tester/src/ReadTester.cpp
@@ -0,0 +1,92 @@
+#include "Model.h"
+
+#include <luci/Importer.h>
+#include <luci/Service/Validate.h>
+#include <luci/Pass/ShapeInferencePass.h>
+#include <luci/Pass/TypeInferencePass.h>
+
+#include <iostream>
+#include <map>
+#include <string>
+
+namespace
+{
+
+void show_help_message(const char *progname, std::ostream &os)
+{
+ os << "USAGE: " << progname << " circlefile" << std::endl << std::endl;
+}
+
+void show_error_message(const char *progname, std::ostream &os, const std::string &msg)
+{
+ os << "ERROR: " << msg << std::endl;
+ os << std::endl;
+
+ show_help_message(progname, os);
+}
+
+} // namespace
+
+/*
+ * @brief ReadTest main
+ *
+ * Give one Circle file as an argument
+ *
+ * This will use luci_import to read the file and get loco graph
+ * In luci_import, LUCI_LOG environment will be checked and will
+ * dump graph to console if set.
+ * i.e. "LUCI_LOG=1 luci_readtester mymodel.circle"
+ */
+int main(int argc, char **argv)
+{
+ if (argc != 2)
+ {
+ show_error_message(argv[0], std::cerr, "Circle file is not specified");
+ return 255;
+ }
+
+ std::string input_path = argv[1];
+
+ std::cout << "[INFO] Circle is '" << input_path << "'" << std::endl;
+
+ // Load model from the file
+ std::unique_ptr<luci::Model> model = luci::load_model(input_path);
+ if (model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load '" << input_path << "'" << std::endl;
+ return 255;
+ }
+
+ const circle::Model *input_model = model->model();
+ if (input_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to read '" << input_path << "'" << std::endl;
+ return 255;
+ }
+
+ luci::Importer importer;
+ auto module = importer.importModule(input_model);
+ assert(module->size() > 0);
+
+ for (size_t g = 0; g < module->size(); ++g)
+ {
+ auto graph = module->graph(g);
+ if (graph == nullptr)
+ return 255;
+
+ {
+ luci::ShapeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+ {
+ luci::TypeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+
+ if (!luci::validate(graph))
+ return 255;
+ }
+ return 0;
+}
diff --git a/compiler/luci/tester/src/WriteTester.cpp b/compiler/luci/tester/src/WriteTester.cpp
new file mode 100644
index 000000000..80019d1b1
--- /dev/null
+++ b/compiler/luci/tester/src/WriteTester.cpp
@@ -0,0 +1,142 @@
+#include "Model.h"
+
+#include <luci/Importer.h>
+#include <luci/Pass/ShapeInferencePass.h>
+#include <luci/Pass/TypeInferencePass.h>
+#include <luci/Service/Validate.h>
+#include <luci/CircleExporter.h>
+#include <oops/InternalExn.h>
+
+#include <fstream>
+#include <iostream>
+#include <map>
+#include <string>
+
+namespace
+{
+
+void show_help_message(const char *progname, std::ostream &os)
+{
+ os << "USAGE: " << progname << " circlefile_in circlefile_out" << std::endl << std::endl;
+}
+
+void show_error_message(const char *progname, std::ostream &os, const std::string &msg)
+{
+ os << "ERROR: " << msg << std::endl;
+ os << std::endl;
+
+ show_help_message(progname, os);
+}
+
+struct CircleExpContract : public luci::CircleExporter::Contract
+{
+public:
+ CircleExpContract(loco::Graph *graph, const std::string &filename)
+ : _graph(graph), _filepath(filename)
+ {
+ // NOTHING TO DO
+ }
+ CircleExpContract(luci::Module *module, const std::string &filename)
+ : _module(module), _filepath(filename)
+ {
+ // NOTHING TO DO
+ }
+ virtual ~CircleExpContract() = default;
+
+public:
+ loco::Graph *graph(void) const final { return _graph; }
+
+ luci::Module *module(void) const final { return _module; }
+
+public:
+ bool store(const char *ptr, const size_t size) const final;
+
+private:
+ loco::Graph *_graph;
+ luci::Module *_module;
+ const std::string _filepath;
+};
+
+bool CircleExpContract::store(const char *ptr, const size_t size) const
+{
+ if (!ptr)
+ INTERNAL_EXN("Graph was not serialized by FlatBuffer for some reason");
+
+ std::ofstream fs(_filepath.c_str(), std::ofstream::binary);
+ fs.write(ptr, size);
+
+ return fs.good();
+}
+
+} // namespace
+
+/*
+ * @brief WriteTester main
+ *
+ * Give two Circle file as an argument
+ *
+ * This will use luci_import to read the first file and get loco graph
+ * With the graph, this will use luci_export to write to the second file
+ * Like ReadTester, LUCI_LOG=1 environment variable is available to dump the graph
+ */
+int main(int argc, char **argv)
+{
+ if (argc != 3)
+ {
+ show_error_message(argv[0], std::cerr, "In/Out Circle file path is not specified");
+ return 255;
+ }
+
+ std::string input_path = argv[1];
+ std::string output_path = argv[2];
+
+ std::cout << "[INFO] Circle from '" << input_path << "' to '" << output_path << "'" << std::endl;
+
+ // Load model from the file
+ std::unique_ptr<luci::Model> model = luci::load_model(input_path);
+ if (model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to load '" << input_path << "'" << std::endl;
+ return 255;
+ }
+
+ const circle::Model *input_model = model->model();
+ if (input_model == nullptr)
+ {
+ std::cerr << "ERROR: Failed to read '" << input_path << "'" << std::endl;
+ return 255;
+ }
+
+ // Import from input Circle file
+ luci::Importer importer;
+ auto module = importer.importModule(input_model);
+ assert(module->size() > 0);
+
+ for (size_t g = 0; g < module->size(); ++g)
+ {
+ auto graph = module->graph(g);
+ if (graph == nullptr)
+ return 255;
+
+ {
+ luci::ShapeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+ {
+ luci::TypeInferencePass pass;
+ while (pass.run(graph) == true)
+ ;
+ }
+
+ if (!luci::validate(graph))
+ return 255;
+ }
+
+ // Export to output Circle file
+ luci::CircleExporter exporter;
+
+ CircleExpContract contract(module.get(), output_path);
+
+ return exporter.invoke(&contract) ? 0 : 255;
+}
diff --git a/compiler/luci/tests/.gitignore b/compiler/luci/tests/.gitignore
new file mode 100644
index 000000000..8dbfa9012
--- /dev/null
+++ b/compiler/luci/tests/.gitignore
@@ -0,0 +1 @@
+/test.local.lst
diff --git a/compiler/luci/tests/CMakeLists.txt b/compiler/luci/tests/CMakeLists.txt
new file mode 100644
index 000000000..4e5639047
--- /dev/null
+++ b/compiler/luci/tests/CMakeLists.txt
@@ -0,0 +1,97 @@
+# TODO use local test.recipe files for small networks
+file(GLOB RECIPES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
+ set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite")
+ set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}"
+ DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+
+ # Generate .tflite
+ add_custom_command(OUTPUT "${RECIPE_OUTPUT_FILE}"
+ COMMAND tflchef-file "${RECIPE_SOURCE_FILE}" "${RECIPE_OUTPUT_FILE}"
+ DEPENDS tflchef-file "${RECIPE_SOURCE_FILE}"
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
+
+ # Generate .circle
+ add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}"
+ COMMAND tflite2circle "${RECIPE_OUTPUT_FILE}" "${CIRCLE_OUTPUT_FILE}"
+ DEPENDS tflite2circle "${RECIPE_OUTPUT_FILE}"
+ COMMENT "Generating ${CIRCLE_OUTPUT_FILE}")
+
+ list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}")
+endforeach(RECIPE)
+
+# Generate from res/TensorFlowLiteRecipes
+nncc_find_resource(TensorFlowLiteRecipes)
+set(TENSORFLOWLITERECIPES_DIR "${TensorFlowLiteRecipes_DIR}")
+
+file(GLOB RECIPES RELATIVE ${TENSORFLOWLITERECIPES_DIR} "${TENSORFLOWLITERECIPES_DIR}/*/test.recipe")
+
+foreach(RECIPE IN ITEMS ${RECIPES})
+ get_filename_component(RECIPE_PREFIX ${RECIPE} DIRECTORY)
+
+ set(RECIPE_SOURCE_FILE "${RECIPE_PREFIX}.recipe")
+ set(RECIPE_OUTPUT_FILE "${RECIPE_PREFIX}.tflite")
+ set(CIRCLE_OUTPUT_FILE "${RECIPE_PREFIX}.circle")
+
+ # Copy .recipe
+ add_custom_command(OUTPUT "${RECIPE_SOURCE_FILE}"
+ COMMAND ${CMAKE_COMMAND} -E copy_if_different
+ "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}" "${RECIPE_SOURCE_FILE}"
+ DEPENDS "${TENSORFLOWLITERECIPES_DIR}/${RECIPE}"
+ COMMENT "Generating ${RECIPE_SOURCE_FILE}")
+
+ # Generate .tflite
+ add_custom_command(OUTPUT "${RECIPE_OUTPUT_FILE}"
+ COMMAND tflchef-file "${RECIPE_SOURCE_FILE}" "${RECIPE_OUTPUT_FILE}"
+ DEPENDS tflchef-file "${RECIPE_SOURCE_FILE}"
+ COMMENT "Generating ${RECIPE_OUTPUT_FILE}")
+
+ # Generate .circle
+ add_custom_command(OUTPUT "${CIRCLE_OUTPUT_FILE}"
+ COMMAND tflite2circle "${RECIPE_OUTPUT_FILE}" "${CIRCLE_OUTPUT_FILE}"
+ DEPENDS tflite2circle "${RECIPE_OUTPUT_FILE}"
+ COMMENT "Generating ${CIRCLE_OUTPUT_FILE}")
+
+ list(APPEND TESTFILES "${CIRCLE_OUTPUT_FILE}")
+endforeach(RECIPE)
+
+# Add a dummy target to create a target-level dependency.
+# TODO Find a way to create dependency between CTest tests (added below) and generated testfiles.
+add_custom_target(luci_testfiles ALL DEPENDS ${TESTFILES})
+
+macro(addread NAME)
+ list(APPEND DAILY_READ_TESTS ${NAME})
+endmacro(addread)
+
+macro(addwrite NAME)
+ list(APPEND DAILY_WRITE_TESTS ${NAME})
+endmacro(addwrite)
+
+# Read "test.lst"
+include("test.lst")
+# Read "test.local.lst" if exists
+include("test.local.lst" OPTIONAL)
+
+add_test(NAME luci_unit_readtest
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/readverify.sh"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "$<TARGET_FILE:luci_readtester>"
+ ${DAILY_READ_TESTS}
+)
+
+add_test(NAME luci_unit_writetest
+ COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/writeverify.sh"
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ "$<TARGET_FILE:luci_writetester>"
+ ${DAILY_WRITE_TESTS}
+)
diff --git a/compiler/luci/tests/readverify.sh b/compiler/luci/tests/readverify.sh
new file mode 100755
index 000000000..3403e9c19
--- /dev/null
+++ b/compiler/luci/tests/readverify.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# This script verifies the basic behavior of luci frontend
+#
+# HOW TO USE
+#
+# ./readverify.sh <path/to/luci_readtester> <TEST 1> <TEST 2> ...
+VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+WORKDIR="$1"; shift
+VERIFY_BINARY_PATH="$1"; shift
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+for TESTCASE in "$@"; do
+ TESTED+=("${TESTCASE}")
+
+ TESTCASE_FILE="${WORKDIR}/${TESTCASE}"
+
+ PASSED_TAG="${TESTCASE_FILE}.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TESTCASE_FILE}.log" <(
+ exec 2>&1
+ set -ex
+
+ "${VERIFY_BINARY_PATH}" "${TESTCASE_FILE}.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("${TESTCASE}")
+ else
+ FAILED+=("${TESTCASE}")
+ fi
+done
+
+if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then
+ echo "FAILED"
+ for TEST in "${FAILED[@]}"
+ do
+ echo "- ${TEST}"
+ done
+ exit 255
+fi
+
+echo "PASSED"
+exit 0
diff --git a/compiler/luci/tests/test.lst b/compiler/luci/tests/test.lst
new file mode 100644
index 000000000..08cbd6b1a
--- /dev/null
+++ b/compiler/luci/tests/test.lst
@@ -0,0 +1,91 @@
+addread(Add_000)
+addread(Add_U8_000)
+addread(ArgMax_000)
+addread(ArgMax_001)
+addread(ArgMax_002)
+addread(ArgMax_003)
+addread(ArgMax_U8_000)
+addread(ArgMax_U8_001)
+addread(ArgMax_U8_002)
+addread(ArgMax_U8_003)
+addread(BatchToSpaceND_000)
+addread(Concatenation_000)
+addread(Concatenation_U8_000)
+addread(Conv2D_000)
+addread(Conv2D_U8_000)
+addread(Conv2D_002)
+addread(Cos_000)
+addread(DepthwiseConv2D_000)
+addread(DepthwiseConv2D_U8_000)
+addread(Div_000)
+addread(Equal_000)
+addread(Exp_000)
+addread(FullyConnected_000)
+addread(FullyConnected_001)
+addread(FullyConnected_U8_000)
+addread(LogicalNot_000)
+addread(LogicalOr_000)
+addread(MaxPool2D_000)
+addread(MaxPool2D_U8_000)
+addread(Mean_000)
+addread(Mul_000)
+addread(Mul_U8_000)
+addread(Pack_000)
+addread(Pack_U8_000)
+addread(Pad_000)
+addread(ReLU_000)
+addread(Reshape_000)
+addread(Reshape_001)
+addread(Reshape_U8_000)
+addread(Rsqrt_000)
+addread(Softmax_000)
+addread(Softmax_U8_000)
+addread(Sub_000)
+addread(Sub_U8_000)
+addread(Transpose_000)
+
+addwrite(Add_000)
+addwrite(Add_U8_000)
+addwrite(ArgMax_000)
+addwrite(ArgMax_001)
+addwrite(ArgMax_002)
+addwrite(ArgMax_003)
+addwrite(ArgMax_U8_000)
+addwrite(ArgMax_U8_001)
+addwrite(ArgMax_U8_002)
+addwrite(ArgMax_U8_003)
+addwrite(BatchToSpaceND_000)
+addwrite(Concatenation_000)
+addwrite(Concatenation_U8_000)
+addwrite(Conv2D_000)
+addwrite(Conv2D_U8_000)
+addwrite(Conv2D_002)
+addwrite(Cos_000)
+addwrite(DepthwiseConv2D_000)
+addwrite(DepthwiseConv2D_U8_000)
+addwrite(Div_000)
+addwrite(Equal_000)
+addwrite(Exp_000)
+addwrite(FullyConnected_000)
+addwrite(FullyConnected_001)
+addwrite(FullyConnected_U8_000)
+addwrite(LogicalNot_000)
+addwrite(LogicalOr_000)
+addwrite(MaxPool2D_000)
+addwrite(MaxPool2D_U8_000)
+addwrite(Mean_000)
+addwrite(Mul_000)
+addwrite(Mul_U8_000)
+addwrite(Pack_000)
+addwrite(Pack_U8_000)
+addwrite(Pad_000)
+addwrite(ReLU_000)
+addwrite(Reshape_000)
+addwrite(Reshape_001)
+addwrite(Reshape_U8_000)
+addwrite(Rsqrt_000)
+addwrite(Softmax_000)
+addwrite(Softmax_U8_000)
+addwrite(Sub_000)
+addwrite(Sub_U8_000)
+addwrite(Transpose_000)
diff --git a/compiler/luci/tests/writeverify.sh b/compiler/luci/tests/writeverify.sh
new file mode 100755
index 000000000..6980bac44
--- /dev/null
+++ b/compiler/luci/tests/writeverify.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+# This script verifies the basic behavior of luci frontend
+#
+# HOW TO USE
+#
+# ./writeverify.sh <path/to/luci_writetester> <TEST 1> <TEST 2> ...
+VERIFY_SOURCE_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+WORKDIR="$1"; shift
+VERIFY_BINARY_PATH="$1"; shift
+
+TESTED=()
+PASSED=()
+FAILED=()
+
+for TESTCASE in "$@"; do
+ TESTED+=("${TESTCASE}")
+
+ TESTCASE_FILE="${WORKDIR}/${TESTCASE}"
+
+ PASSED_TAG="${TESTCASE_FILE}_w.passed"
+ rm -f "${PASSED_TAG}"
+
+ cat > "${TESTCASE_FILE}_w.log" <(
+ exec 2>&1
+ set -ex
+
+ "${VERIFY_BINARY_PATH}" "${TESTCASE_FILE}.circle" "${TESTCASE_FILE}_w.circle"
+
+ if [[ $? -eq 0 ]]; then
+ touch "${PASSED_TAG}"
+ fi
+ )
+
+ if [[ -f "${PASSED_TAG}" ]]; then
+ PASSED+=("${TESTCASE}")
+ else
+ FAILED+=("${TESTCASE}")
+ fi
+done
+
+if [[ ${#TESTED[@]} -ne ${#PASSED[@]} ]]; then
+ echo "FAILED"
+ for TEST in "${FAILED[@]}"
+ do
+ echo "- ${TEST}"
+ done
+ exit 255
+fi
+
+echo "PASSED"
+exit 0